Compare commits

..
10 Commits
Author SHA1 Message Date
Laan Tungir aa07fe0edd v2.1.35 - Style native date/time pickers to match admin monospace theme 2026-08-02 17:48:59 -04:00
Laan Tungir 67874636d2 v2.1.34 - Replace date pickers with plain text datetime inputs (YYYY-MM-DD HH:MM) for cleanup page 2026-08-02 17:45:46 -04:00
Laan Tungir c316c39860 v2.1.33 - Replace max_age_days with from_date/to_date date range pickers in cleanup page 2026-08-02 17:39:42 -04:00
Laan Tungir a4197dab4b v2.1.32 - Clear stats cache after cleanup delete so dashboard reflects changes immediately 2026-08-02 17:37:14 -04:00
Laan Tungir 8453d82ee4 v2.1.31 - Fix make_and_restart_relay.sh: load test keys by default, add --no-test-keys flag 2026-08-02 17:12:52 -04:00
Laan Tungir 8c6770c7e2 v2.1.30 - Add Event Cleanup admin page with saved queries, preview, and delete execution 2026-08-02 17:08:03 -04:00
Laan Tungir 7cd33aa6ea v2.1.29 - Remove SQLite support, hardcode PostgreSQL backend, fix admin API to use first_seen, add pgweb systemd service 2026-08-02 08:20:11 -04:00
Laan Tungir 81c5339063 v2.1.28 - Add kind-3 fallback for relay discovery (NIP-65 pre-standard relay lists in contact list r tags) 2026-07-31 13:58:24 -04:00
Laan Tungir 5ec5ed9bd4 v2.1.27 - Caching page: active backfill row highlight (red border), increase follows limit to 1000, accept npub or hex for caching_root_npubs, fix static caching binary build with PostgreSQL 2026-07-31 11:42:27 -04:00
Laan Tungir bd5150cd09 v2.1.26 - Accept both npub and hex format for caching_root_npubs, fix static caching binary build with PostgreSQL support 2026-07-31 11:23:09 -04:00
94 changed files with 27365 additions and 11012 deletions
+53
View File
@@ -2,6 +2,59 @@
**Project-Specific Information for AI Agents Working with C-Relay-PG**
## ⚠️ CRITICAL: Never Run `make` Directly
**NEVER run `make` to build the relay binary.** The Makefile will refuse and
print an error. Running `make` directly produces a dynamically-linked binary
that will silently fall back to SQLite storage while the admin UI connects to
PostgreSQL — causing the admin page to show stale data.
Always use one of these instead:
- **`./build_static.sh`** — Build the static MUSL binary with PostgreSQL backend
- **`./make_and_restart_relay.sh`** — Build, kill old relay, and start new one
The Makefile exists only for submodule compilation (nostr_core_lib, c_utils_lib)
and utility targets (clean, install-deps). It will refuse to build the relay
binary directly.
## Critical Build Commands
### Primary Build Command
```bash
./make_and_restart_relay.sh
```
**Never use `make` directly.** The project requires the custom restart script which:
- Handles database preservation/cleanup based on flags
- Manages architecture-specific binary detection (x86/ARM64)
- Performs automatic process cleanup and port management
- Starts relay in background with proper logging
**Note:** `--test-keys` / `-t` is now the **default**. Use `--no-test-keys` or `--production` for random key generation.
### Architecture-Specific Binary Outputs
- **x86_64**: `./build/c_relay_pg_x86`
- **ARM64**: `./build/c_relay_pg_arm64`
- **Other**: `./build/c_relay_pg_$(ARCH)`
**Project-Specific Information for AI Agents Working with C-Relay-PG**
## ⚠️ CRITICAL: Never Run `make` Directly
**NEVER run `make` to build the relay binary.** The Makefile will refuse and
print an error. Running `make` directly produces a dynamically-linked binary
that will silently fall back to SQLite storage while the admin UI connects to
PostgreSQL — causing the admin page to show stale data.
Always use one of these instead:
- **`./build_static.sh`** — Build the static MUSL binary with PostgreSQL backend
- **`./make_and_restart_relay.sh`** — Build, kill old relay, and start new one
The Makefile exists only for submodule compilation (nostr_core_lib, c_utils_lib)
and utility targets (clean, install-deps). It will refuse to build the relay
binary directly.
## Critical Build Commands
### Primary Build Command
+9 -15
View File
@@ -1,14 +1,13 @@
# Alpine-based MUSL static binary builder for C-Relay-PG
# Produces truly portable binaries with zero runtime dependencies
# PostgreSQL backend only.
ARG DEBUG_BUILD=false
ARG DB_BACKEND=sqlite
FROM alpine:3.19 AS builder
# Re-declare build arguments in this stage
ARG DEBUG_BUILD=false
ARG DB_BACKEND=sqlite
# Install build dependencies
RUN apk add --no-cache \
@@ -105,7 +104,8 @@ COPY Makefile /build/Makefile
# Build c-relay-pg with full static linking (only rebuilds when src/ changes)
# Disable fortification to avoid __*_chk symbols that don't exist in MUSL
# Use conditional compilation flags based on DEBUG_BUILD and DB_BACKEND build args
# Use conditional compilation flags based on DEBUG_BUILD build arg.
# PostgreSQL backend is always used.
RUN if [ "$DEBUG_BUILD" = "true" ]; then \
CFLAGS="-g -O2 -DDEBUG"; \
STRIP_CMD="echo 'Keeping debug symbols'"; \
@@ -115,15 +115,9 @@ RUN if [ "$DEBUG_BUILD" = "true" ]; then \
STRIP_CMD="strip /build/c_relay_pg_static"; \
echo "Building optimized production binary (symbols stripped)"; \
fi && \
if [ "$DB_BACKEND" = "postgres" ]; then \
DB_FLAGS="-DDB_BACKEND_POSTGRES -DHAVE_LIBPQ"; \
DB_LIBS="-lpq -lpgcommon -lpgport"; \
echo "Compiling with PostgreSQL backend"; \
else \
DB_FLAGS=""; \
DB_LIBS=""; \
echo "Compiling with SQLite backend"; \
fi && \
DB_FLAGS="-DDB_BACKEND_POSTGRES -DHAVE_LIBPQ" && \
DB_LIBS="-lpq -lpgcommon -lpgport" && \
echo "Compiling with PostgreSQL backend" && \
gcc -static $CFLAGS $DB_FLAGS -Wall -Wextra -std=c99 \
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
-I. -Ic_utils_lib/src -Inostr_core_lib -Inostr_core_lib/nostr_core \
@@ -132,11 +126,11 @@ RUN if [ "$DEBUG_BUILD" = "true" ]; then \
src/nip009.c src/nip011.c src/nip013.c src/nip040.c src/nip042.c \
src/websockets.c src/subscriptions.c src/api.c src/embedded_web_content.c src/ip_ban.c \
src/caching_inbox_poller.c src/caching_service_launcher.c \
src/db_ops.c src/db_ops_sqlite.c src/db_ops_postgres.c src/thread_pool.c \
src/db_ops.c src/db_ops_postgres.c src/thread_pool.c \
-o /build/c_relay_pg_static \
c_utils_lib/libc_utils.a \
nostr_core_lib/libnostr_core_x64.a \
-lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \
-lwebsockets -lssl -lcrypto -lsecp256k1 \
-lcurl -lz -lpthread -lm -ldl $DB_LIBS && \
eval "$STRIP_CMD"
@@ -150,4 +144,4 @@ RUN echo "=== Binary Information ===" && \
# Output stage - just the binary
FROM scratch AS output
COPY --from=builder /build/c_relay_pg_static /c_relay_pg_static
COPY --from=builder /build/c_relay_pg_static /c_relay_pg_static
+75 -135
View File
@@ -1,26 +1,16 @@
# C-Relay-PG Makefile
CC = gcc
CFLAGS = -Wall -Wextra -std=c99 -g -O2
CFLAGS = -Wall -Wextra -std=c99 -g -O2 -DDB_BACKEND_POSTGRES -DHAVE_LIBPQ
INCLUDES = -I. -Ic_utils_lib/src -Inostr_core_lib -Inostr_core_lib/nostr_core -Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket -I/usr/include/postgresql
LIBS = -lsqlite3 -lwebsockets -lz -ldl -lpthread -lm -L/usr/local/lib -lsecp256k1 -lssl -lcrypto -L/usr/local/lib -lcurl -Lc_utils_lib -lc_utils
DB_BACKEND ?= sqlite
LIBS = -lwebsockets -lz -ldl -lpthread -lm -L/usr/local/lib -lsecp256k1 -lssl -lcrypto -L/usr/local/lib -lcurl -Lc_utils_lib -lc_utils -lpq
# Build directory
BUILD_DIR = build
# Source files
MAIN_SRC = src/main.c src/config.c src/dm_admin.c src/request_validator.c src/nip009.c src/nip011.c src/nip013.c src/nip040.c src/nip042.c src/websockets.c src/subscriptions.c src/api.c src/embedded_web_content.c src/ip_ban.c src/thread_pool.c src/caching_inbox_poller.c src/caching_service_launcher.c
DB_OPS_SRC = src/db_ops.c
ifeq ($(DB_BACKEND),postgres)
CFLAGS += -DDB_BACKEND_POSTGRES -DHAVE_LIBPQ
DB_OPS_SRC += src/db_ops_postgres.c
LIBS += -lpq
else
DB_OPS_SRC += src/db_ops_sqlite.c
endif
DB_OPS_SRC = src/db_ops.c src/db_ops_postgres.c
NOSTR_CORE_LIB = nostr_core_lib/libnostr_core_x64.a
C_UTILS_LIB = c_utils_lib/libc_utils.a
@@ -37,8 +27,30 @@ else
TARGET = $(BUILD_DIR)/c_relay_pg_$(ARCH)
endif
# Default target
all: $(TARGET)
# Default target — refuse direct build, instruct to use build_static.sh
# The Makefile is only for submodule builds (nostr_core_lib, c_utils_lib)
# and utility targets (clean, install-deps, etc.).
# Running 'make' directly produces a dynamically-linked binary that will
# silently fall back to SQLite storage while the admin UI connects to
# PostgreSQL — causing the admin page to show stale data.
all:
@echo "============================================"
@echo " ERROR: Do not run 'make' directly!"
@echo ""
@echo " This project requires a static MUSL build"
@echo " with PostgreSQL backend. Run:"
@echo ""
@echo " ./build_static.sh"
@echo ""
@echo " Or use the full build+restart script:"
@echo ""
@echo " ./make_and_restart_relay.sh"
@echo ""
@echo " The Makefile is only for submodule builds"
@echo " (nostr_core_lib, c_utils_lib) and utility"
@echo " targets (clean, install-deps, etc.)."
@echo "============================================"
@exit 1
# Create build directory
$(BUILD_DIR):
@@ -92,147 +104,75 @@ force-version:
@echo "Force updating main.h version information..."
@$(MAKE) src/main.h
# Build the relay
$(TARGET): $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(DB_OPS_SRC) $(NOSTR_CORE_LIB) $(C_UTILS_LIB)
@echo "Compiling C-Relay-PG for architecture: $(ARCH) (backend: $(DB_BACKEND))"
$(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) $(DB_OPS_SRC) -o $(TARGET) $(NOSTR_CORE_LIB) $(C_UTILS_LIB) $(LIBS)
@echo "Build complete: $(TARGET)"
# Build the relay — guarded to prevent accidental use.
# Use ./build_static.sh instead.
$(TARGET):
@echo "ERROR: Do not run 'make $(TARGET)' directly."
@echo "Use ./build_static.sh to build the static MUSL binary."
@exit 1
# Build for specific architectures
x86: $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(DB_OPS_SRC) $(NOSTR_CORE_LIB) $(C_UTILS_LIB)
@echo "Building C-Relay-PG for x86_64 (backend: $(DB_BACKEND))..."
$(CC) $(CFLAGS) $(INCLUDES) $(MAIN_SRC) $(DB_OPS_SRC) -o $(BUILD_DIR)/c_relay_pg_x86 $(NOSTR_CORE_LIB) $(C_UTILS_LIB) $(LIBS)
@echo "Build complete: $(BUILD_DIR)/c_relay_pg_x86"
x86:
@echo "ERROR: Do not run 'make x86' directly."
@echo "Use ./build_static.sh to build the static MUSL binary."
@exit 1
arm64: $(BUILD_DIR) src/main.h src/sql_schema.h $(MAIN_SRC) $(DB_OPS_SRC) $(NOSTR_CORE_LIB) $(C_UTILS_LIB)
@echo "Cross-compiling C-Relay-PG for ARM64 (backend: $(DB_BACKEND))..."
arm64:
@echo "ERROR: Do not run 'make arm64' directly."
@echo "Use ./build_static.sh to build the static MUSL binary."
@exit 1
@if ! command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \
echo "ERROR: ARM64 cross-compiler not found."; \
echo "Install with: make install-cross-tools"; \
echo "Or install manually: sudo apt install gcc-aarch64-linux-gnu"; \
echo "ERROR: aarch64-linux-gnu-gcc not found."; \
echo "Install the ARM64 cross-compiler:"; \
echo " sudo apt install gcc-aarch64-linux-gnu"; \
exit 1; \
fi
@echo "Checking for ARM64 development libraries..."
@if ! dpkg -l | grep -q "libssl-dev:arm64\|libsqlite3-dev:arm64"; then \
@if ! dpkg -l | grep -q "libssl-dev:arm64\|libpq-dev:arm64"; then \
echo "ERROR: ARM64 libraries not found. Cross-compilation requires ARM64 versions of:"; \
echo " - libssl-dev:arm64"; \
echo " - libsqlite3-dev:arm64"; \
echo " - libpq-dev:arm64"; \
echo " - libwebsockets-dev:arm64"; \
echo " - libsecp256k1-dev:arm64"; \
echo " - zlib1g-dev:arm64"; \
echo " - libcurl4-openssl-dev:arm64"; \
echo " - libcurl-dev:arm64"; \
echo ""; \
echo "Install ARM64 libraries with: make install-arm64-deps"; \
echo "Or use Docker for cross-platform builds."; \
echo "Install them with:"; \
echo " sudo dpkg --add-architecture arm64"; \
echo " sudo apt update"; \
echo " sudo apt install gcc-aarch64-linux-gnu \\"; \
echo " libssl-dev:arm64 libpq-dev:arm64 \\"; \
echo " libwebsockets-dev:arm64 zlib1g-dev:arm64 \\"; \
echo " libcurl4-openssl-dev:arm64"; \
exit 1; \
fi
@echo "Using aarch64-linux-gnu-gcc with ARM64 libraries..."
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig \
aarch64-linux-gnu-gcc $(CFLAGS) $(INCLUDES) $(MAIN_SRC) $(DB_OPS_SRC) -o $(BUILD_DIR)/c_relay_pg_arm64 $(NOSTR_CORE_LIB) $(C_UTILS_LIB) \
-L/usr/lib/aarch64-linux-gnu $(LIBS)
@echo "Build complete: $(BUILD_DIR)/c_relay_pg_arm64"
fi; \
PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig \
PKG_CONFIG_SYSROOT_DIR=/ \
aarch64-linux-gnu-gcc -Wall -Wextra -std=c99 -g -O2 \
-DDB_BACKEND_POSTGRES -DHAVE_LIBPQ \
-I. -Ic_utils_lib/src -Inostr_core_lib -Inostr_core_lib/nostr_core \
-Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \
$(MAIN_SRC) $(DB_OPS_SRC) \
-o $(BUILD_DIR)/c_relay_pg_arm64 \
$(NOSTR_CORE_LIB) $(C_UTILS_LIB) \
-lwebsockets -lssl -lcrypto -lz -ldl -lpthread -lm \
-lsecp256k1 -lcurl -lpq
@echo "Cross-compilation complete: $(BUILD_DIR)/c_relay_pg_arm64"
# Install ARM64 cross-compilation dependencies
# Install dependencies for ARM64 cross-compilation
install-arm64-deps:
@echo "Installing ARM64 cross-compilation dependencies..."
@echo "This requires adding ARM64 architecture and installing cross-libraries..."
sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install -y \
gcc-aarch64-linux-gnu \
libc6-dev-arm64-cross \
libssl-dev:arm64 \
libsqlite3-dev:arm64 \
zlib1g-dev:arm64 \
sudo apt install -y gcc-aarch64-linux-gnu \
libssl-dev:arm64 libpq-dev:arm64 \
libwebsockets-dev:arm64 zlib1g-dev:arm64 \
libcurl4-openssl-dev:arm64
@echo "Note: libwebsockets-dev:arm64 and libsecp256k1-dev:arm64 may need manual building"
# Install cross-compilation tools
install-cross-tools:
@echo "Installing cross-compilation tools..."
# Install dependencies for native build
install-deps:
sudo apt update
sudo apt install -y gcc-aarch64-linux-gnu libc6-dev-arm64-cross
# Check what architectures we can actually build
check-toolchain:
@echo "Checking available toolchains:"
@echo "Native compiler: $(shell $(CC) --version | head -1)"
@if command -v aarch64-linux-gnu-gcc >/dev/null 2>&1; then \
echo "ARM64 cross-compiler: $(shell aarch64-linux-gnu-gcc --version | head -1)"; \
else \
echo "ARM64 cross-compiler: NOT INSTALLED (install with 'make install-cross-tools')"; \
fi
# Run tests
test: $(TARGET)
@echo "Running tests..."
./tests/1_nip_test.sh
# Initialize database (now handled automatically when server starts)
init-db:
@echo "Database initialization is now handled automatically when the server starts."
@echo "The schema is embedded in the binary - no external files needed."
@echo "To manually recreate database: rm -f db/c_nostr_relay.db && ./build/c_relay_pg_x86"
sudo apt install -y build-essential libpq-dev libssl-dev libcurl4-openssl-dev libsecp256k1-dev zlib1g-dev jq curl
# Clean build artifacts
clean:
rm -rf $(BUILD_DIR)
@echo "Clean complete"
# Clean everything including nostr_core_lib and c_utils_lib
clean-all: clean
cd nostr_core_lib && make clean 2>/dev/null || true
cd c_utils_lib && make clean 2>/dev/null || true
# Install dependencies (Ubuntu/Debian)
install-deps:
@echo "Installing dependencies..."
sudo apt update
sudo apt install -y build-essential libsqlite3-dev libssl-dev libcurl4-openssl-dev libsecp256k1-dev zlib1g-dev jq curl
# Help
help:
@echo "C-Relay-PG Build System"
@echo ""
@echo "Targets:"
@echo " all Build the relay for current architecture (default)"
@echo " x86 Build specifically for x86_64"
@echo " arm64 Build for ARM64 (requires cross-compilation setup)"
@echo " test Build and run tests"
@echo " init-db Initialize the database"
@echo " clean Clean build artifacts"
@echo " clean-all Clean everything including dependencies"
@echo " install-deps Install system dependencies"
@echo " install-cross-tools Install basic ARM64 cross-compiler"
@echo " install-arm64-deps Install ARM64 cross-compilation libraries"
@echo " check-toolchain Check available compilers"
@echo " help Show this help"
@echo ""
@echo "Usage:"
@echo " make # Build the relay for current arch"
@echo " make x86 # Build for x86_64"
@echo " make arm64 # Build for ARM64 (fails if cross-compilation not set up)"
@echo " make install-arm64-deps # Install full ARM64 cross-compilation setup"
@echo " make check-toolchain # Check what compilers are available"
@echo " make test # Run tests"
@echo " make init-db # Set up database"
@echo " make force-version # Force regenerate main.h from git"
# Build fully static MUSL binaries using Docker
static-musl-x86_64:
@echo "Building fully static MUSL binary for x86_64..."
docker buildx build --platform linux/amd64 -f examples/deployment/static-builder.Dockerfile -t c-relay-pg-static-builder-x86_64 --load .
docker run --rm -v $(PWD)/build:/output c-relay-pg-static-builder-x86_64 sh -c "cp /c_relay_pg_static_musl_x86_64 /output/"
@echo "Static binary created: build/c_relay_pg_static_musl_x86_64"
static-musl-arm64:
@echo "Building fully static MUSL binary for ARM64..."
docker buildx build --platform linux/arm64 -f examples/deployment/static-builder.Dockerfile -t c-relay-pg-static-builder-arm64 --load .
docker run --rm -v $(PWD)/build:/output c-relay-pg-static-builder-arm64 sh -c "cp /c_relay_pg_static_musl_x86_64 /output/c_relay_pg_static_musl_arm64"
@echo "Static binary created: build/c_relay_pg_static_musl_arm64"
static-musl: static-musl-x86_64 static-musl-arm64
@echo "Built static MUSL binaries for both architectures"
.PHONY: static-musl-x86_64 static-musl-arm64 static-musl
.PHONY: all x86 arm64 test init-db clean clean-all install-deps install-cross-tools install-arm64-deps check-toolchain help force-version
.PHONY: all x86 arm64 install-arm64-deps install-deps clean force-version
+32 -2
View File
@@ -87,8 +87,27 @@ try {
$inbox = $pdo->query("SELECT COUNT(*) AS pending, COUNT(*) FILTER (WHERE source_class='live') AS live, COUNT(*) FILTER (WHERE source_class='backfill') AS backfill FROM caching_event_inbox")->fetch() ?: $inbox;
} catch (PDOException $e) {}
// Upstream relay status (per-relay connection state)
$upstreamRelays = [];
try {
$upstreamRelays = $pdo->query("
SELECT relay_url, status_code, status_text, updated_at
FROM caching_upstream_relays
ORDER BY relay_url
")->fetchAll();
} catch (PDOException $e) {}
// Follows (paginated, with names from profiles cache + per-relay progress)
$page = max(1, (int)($_GET['page'] ?? 1));
$perPage = 50;
$offset = ($page - 1) * $perPage;
$follows = [];
$totalFollows = 0;
try {
$totalFollows = (int)$pdo->query("SELECT COUNT(*) FROM caching_followed_pubkeys")->fetchColumn();
} catch (PDOException $e) {}
try {
$follows = $pdo->query("
SELECT fp.pubkey, fp.is_root, fp.backfill_complete, fp.events_fetched,
@@ -99,7 +118,8 @@ try {
(SELECT count(*) FROM caching_backfill_relay_progress WHERE author_pubkey = fp.pubkey AND complete = false) AS relay_incomplete
FROM caching_followed_pubkeys fp
LEFT JOIN profiles p ON p.pubkey = fp.pubkey
ORDER BY fp.is_root DESC, fp.events_fetched DESC LIMIT 100
ORDER BY fp.is_root DESC, fp.events_fetched DESC
LIMIT $perPage OFFSET $offset
")->fetchAll();
// Fetch per-relay progress for all followed pubkeys in one query
$relayProgress = [];
@@ -120,4 +140,14 @@ try {
}
} catch (PDOException $e) {}
json_response(['state' => $state, 'active' => $active, 'inbox' => $inbox, 'follows' => $follows, 'config' => $config]);
json_response([
'state' => $state,
'active' => $active,
'inbox' => $inbox,
'follows' => $follows,
'config' => $config,
'upstreamRelays' => $upstreamRelays,
'totalFollows' => $totalFollows,
'page' => $page,
'perPage' => $perPage,
]);
+20 -3
View File
@@ -1,6 +1,6 @@
<?php
/**
* admin2/api/chart.php — Standalone ASCII chart endpoint.
* admin/api/chart.php — Standalone ASCII chart endpoint.
*
* Returns a plain-text ASCII X-bar chart of event counts over time.
* Works in both the browser (injected into a <div>) and the terminal:
@@ -10,8 +10,22 @@
* curl http://localhost:8088/api/chart.php?range=month
* curl http://localhost:8088/api/chart.php?range=year
*
* This endpoint is read-only (a fixed aggregate COUNT query with the only
* user input being the `range` selector, which is validated against a
* whitelist). It is safe to expose publicly and is served without auth at:
*
* https://<domain>/relay/api/chart.php?range=day
*
* It is also reachable (behind Basic Auth) from the admin UI at:
*
* https://<domain>/relay/admin/api/chart.php?range=day
*
* Caching: the hour chart is never cached (live). Day/month/year are
* cached to file with TTLs to avoid expensive queries on every request.
*
* NOTE: Uses first_seen (not created_at) for binning, because some events
* have corrupted created_at timestamps (far-future values) that would
* place them outside the visible chart range.
*/
require_once __DIR__ . '/../lib/helpers.php';
@@ -59,9 +73,12 @@ try {
// bin index 0 = oldest, bin index (num_bins-1) = newest
$base_bin = (int)floor($epoch / $bin_size);
$sql = "SELECT FLOOR(created_at / {$bin_size})::BIGINT - {$base_bin} AS bin, COUNT(*) AS cnt
// Use first_seen (not created_at) for binning, because some events
// have corrupted created_at timestamps (far-future values) that
// would place them outside the visible chart range.
$sql = "SELECT FLOOR(first_seen / {$bin_size})::BIGINT - {$base_bin} AS bin, COUNT(*) AS cnt
FROM events
WHERE created_at >= {$epoch}
WHERE first_seen >= {$epoch}
GROUP BY bin
ORDER BY bin";
$rows = $pdo->query($sql)->fetchAll();
+310
View File
@@ -0,0 +1,310 @@
<?php
/**
* admin2/api/cleanup.php — Event Cleanup: Preview & Execute.
*
* GET — Preview a cleanup query (count + size estimate, no delete).
* POST — Execute a cleanup query (dry_run=true for preview, false for delete).
*/
require_once __DIR__ . '/../lib/helpers.php';
$pdo = db();
$method = $_SERVER['REQUEST_METHOD'];
// ── Shared filter builder ──────────────────────────────────────────────
// Returns [sql_conditions[], params[]] for the WHERE clauses.
function build_filters(array $opts): array {
$conds = [];
$params = [];
// Kinds filter
if (!empty($opts['kinds'])) {
$kinds = is_array($opts['kinds']) ? $opts['kinds'] : explode(',', $opts['kinds']);
$kinds = array_map('intval', $kinds);
$kinds = array_filter($kinds, fn($v) => $v > 0);
if (!empty($kinds)) {
$placeholders = [];
foreach ($kinds as $i => $k) {
$key = ':kind_' . $i;
$placeholders[] = $key;
$params[$key] = $k;
}
$conds[] = 'e.kind IN (' . implode(',', $placeholders) . ')';
}
}
// Date/time range filter (supports YYYY-MM-DD or YYYY-MM-DD HH:MM)
$from_date = $opts['from_date'] ?? '';
$to_date = $opts['to_date'] ?? '';
if ($from_date !== '') {
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $from_date)) {
$conds[] = 'e.created_at >= EXTRACT(EPOCH FROM :from_date::date)::BIGINT';
$params[':from_date'] = $from_date;
} elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $from_date)) {
$conds[] = 'e.created_at >= EXTRACT(EPOCH FROM :from_date::timestamp)::BIGINT';
$params[':from_date'] = $from_date . ':00';
}
}
if ($to_date !== '') {
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $to_date)) {
$conds[] = 'e.created_at < (EXTRACT(EPOCH FROM :to_date::date)::BIGINT + 86400)';
$params[':to_date'] = $to_date;
} elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $to_date)) {
$conds[] = 'e.created_at < EXTRACT(EPOCH FROM :to_date::timestamp)::BIGINT';
$params[':to_date'] = $to_date . ':00';
}
}
// Follows filter
$follows_filter = $opts['follows_filter'] ?? 'all';
if ($follows_filter === 'follows') {
$conds[] = 'e.pubkey IN (SELECT pubkey FROM caching_followed_pubkeys)';
} elseif ($follows_filter === 'non_follows') {
$conds[] = 'e.pubkey NOT IN (SELECT pubkey FROM caching_followed_pubkeys)';
}
return [$conds, $params];
}
// ── Build a WHERE clause string from conditions ────────────────────────
function where_clause(array $conds): string {
if (empty($conds)) return '';
return 'AND ' . implode("\n AND ", $conds);
}
// ── Format bytes as human-readable ─────────────────────────────────────
function format_bytes(int $bytes): string {
if ($bytes < 1024) return $bytes . ' B';
if ($bytes < 1048576) return round($bytes / 1024, 1) . ' KB';
if ($bytes < 1073741824) return round($bytes / 1048576, 1) . ' MB';
return round($bytes / 1073741824, 2) . ' GB';
}
// ── GET: Preview ───────────────────────────────────────────────────────
if ($method === 'GET') {
$opts = [
'follows_filter' => $_GET['follows_filter'] ?? 'all',
'kinds' => $_GET['kinds'] ?? '',
'from_date' => $_GET['from_date'] ?? '',
'to_date' => $_GET['to_date'] ?? '',
'max_events' => intval($_GET['max_events'] ?? 0),
];
list($conds, $params) = build_filters($opts);
$where = where_clause($conds);
// Preview SQL (for display)
$sql_preview = "SELECT COUNT(*) AS match_count,\n"
. " COALESCE(SUM(pg_column_size(event_json)), 0) AS total_size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1\n"
. ($where ? " $where\n" : '');
// Count + size query
$count_sql = "SELECT COUNT(*) AS match_count,\n"
. " COALESCE(SUM(pg_column_size(event_json)), 0) AS total_size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1 $where";
try {
$stmt = $pdo->prepare($count_sql);
$stmt->execute($params);
$row = $stmt->fetch();
$match_count = intval($row['match_count'] ?? 0);
$total_size_bytes = intval($row['total_size_bytes'] ?? 0);
} catch (PDOException $e) {
json_response(['error' => 'Preview query failed: ' . $e->getMessage()]);
exit;
}
// Kind breakdown
$breakdown = [];
if ($match_count > 0) {
$breakdown_sql = "SELECT e.kind,\n"
. " COUNT(*) AS count,\n"
. " COALESCE(SUM(pg_column_size(e.event_json)), 0) AS size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1 $where\n"
. "GROUP BY e.kind\n"
. "ORDER BY count DESC\n"
. "LIMIT 50";
try {
$stmt = $pdo->prepare($breakdown_sql);
$stmt->execute($params);
$rows = $stmt->fetchAll();
foreach ($rows as $r) {
$breakdown[] = [
'kind' => intval($r['kind']),
'count' => intval($r['count']),
'size_bytes' => intval($r['size_bytes']),
];
}
} catch (PDOException $e) {
// Breakdown is non-critical
}
}
// If a query_id was provided, update last_preview_count and last_preview_size_bytes
$query_id = intval($_GET['query_id'] ?? 0);
if ($query_id > 0) {
try {
$pdo->prepare(
"UPDATE cleanup_saved_queries\n"
. " SET last_preview_count = :count,\n"
. " last_preview_size_bytes = :size\n"
. " WHERE id = :id"
)->execute([
':id' => $query_id,
':count' => $match_count,
':size' => $total_size_bytes,
]);
} catch (PDOException $e) {}
}
json_response([
'match_count' => $match_count,
'total_size_bytes' => $total_size_bytes,
'total_size_human' => format_bytes($total_size_bytes),
'avg_size_per_event' => $match_count > 0 ? intval($total_size_bytes / $match_count) : 0,
'kinds_breakdown' => $breakdown,
'sql_preview' => $sql_preview,
]);
exit;
}
// ── POST: Execute (or dry-run) ─────────────────────────────────────────
if ($method === 'POST') {
$body = json_decode(file_get_contents('php://input'), true);
if (!$body) {
json_response(['error' => 'Invalid JSON body']);
exit;
}
$dry_run = !empty($body['dry_run']);
$opts = [
'follows_filter' => $body['follows_filter'] ?? 'all',
'kinds' => $body['kinds'] ?? [],
'from_date' => $body['from_date'] ?? '',
'to_date' => $body['to_date'] ?? '',
'max_events' => intval($body['max_events'] ?? 0),
];
list($conds, $params) = build_filters($opts);
$where = where_clause($conds);
// If dry_run, return preview (same as GET)
if ($dry_run) {
$count_sql = "SELECT COUNT(*) AS match_count,\n"
. " COALESCE(SUM(pg_column_size(event_json)), 0) AS total_size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1 $where";
try {
$stmt = $pdo->prepare($count_sql);
$stmt->execute($params);
$row = $stmt->fetch();
$match_count = intval($row['match_count'] ?? 0);
$total_size_bytes = intval($row['total_size_bytes'] ?? 0);
} catch (PDOException $e) {
json_response(['error' => 'Preview query failed: ' . $e->getMessage()]);
exit;
}
// Kind breakdown
$breakdown = [];
if ($match_count > 0) {
$breakdown_sql = "SELECT e.kind, COUNT(*) AS count,\n"
. " COALESCE(SUM(pg_column_size(e.event_json)), 0) AS size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1 $where\n"
. "GROUP BY e.kind\n"
. "ORDER BY count DESC\n"
. "LIMIT 50";
try {
$stmt = $pdo->prepare($breakdown_sql);
$stmt->execute($params);
foreach ($stmt->fetchAll() as $r) {
$breakdown[] = [
'kind' => intval($r['kind']),
'count' => intval($r['count']),
'size_bytes' => intval($r['size_bytes']),
];
}
} catch (PDOException $e) {}
}
json_response([
'match_count' => $match_count,
'total_size_bytes' => $total_size_bytes,
'total_size_human' => format_bytes($total_size_bytes),
'avg_size_per_event' => $match_count > 0 ? intval($total_size_bytes / $match_count) : 0,
'kinds_breakdown' => $breakdown,
'dry_run' => true,
]);
exit;
}
// ── Actual DELETE ──────────────────────────────────────────────────
$max_events = intval($body['max_events'] ?? 0);
$limit_clause = $max_events > 0 ? 'LIMIT :max_events' : '';
// First, get the count and size of what will be deleted
$preview_sql = "SELECT COUNT(*) AS match_count,\n"
. " COALESCE(SUM(pg_column_size(event_json)), 0) AS total_size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1 $where";
try {
$stmt = $pdo->prepare($preview_sql);
$stmt->execute($params);
$row = $stmt->fetch();
$expected_count = intval($row['match_count'] ?? 0);
$expected_bytes = intval($row['total_size_bytes'] ?? 0);
} catch (PDOException $e) {
json_response(['error' => 'Pre-delete count failed: ' . $e->getMessage()]);
exit;
}
// Build the DELETE using id IN (subquery) for safe LIMIT + ORDER BY
$delete_sql = "DELETE FROM events e\n"
. "WHERE e.id IN (\n"
. " SELECT e2.id FROM events e2\n"
. " WHERE 1=1 $where\n"
. " ORDER BY e2.created_at ASC\n"
. " $limit_clause\n"
. ")";
$start_time = microtime(true);
try {
$stmt = $pdo->prepare($delete_sql);
if ($max_events > 0) {
$params[':max_events'] = $max_events;
}
$stmt->execute($params);
$deleted_count = $stmt->rowCount();
} catch (PDOException $e) {
json_response(['error' => 'Delete query failed: ' . $e->getMessage()]);
exit;
}
$duration_ms = round((microtime(true) - $start_time) * 1000);
// If we deleted fewer than expected, the actual freed bytes are proportional
$freed_bytes = $expected_count > 0
? intval($expected_bytes * ($deleted_count / $expected_count))
: 0;
// Clear stats cache so the dashboard reflects changes immediately
$cache_dir = __DIR__ . '/../cache';
foreach (['stats_kinds.json', 'stats_pubkeys.json'] as $cache_file) {
$path = $cache_dir . '/' . $cache_file;
if (is_file($path)) @unlink($path);
}
json_response([
'deleted_count' => $deleted_count,
'freed_bytes' => $freed_bytes,
'freed_human' => format_bytes($freed_bytes),
'duration_ms' => $duration_ms,
]);
exit;
}
// Unsupported method
json_response(['error' => 'Method not allowed']);
+345
View File
@@ -0,0 +1,345 @@
<?php
/**
* admin2/api/cleanup_queries.php — Saved Cleanup Query CRUD.
*
* GET — List all saved queries.
* POST — Create/update/delete/execute saved queries.
*/
require_once __DIR__ . '/../lib/helpers.php';
$pdo = db();
$method = $_SERVER['REQUEST_METHOD'];
/** Format bytes as human-readable. */
function fmt_bytes(int $bytes): string {
if ($bytes < 1024) return $bytes . ' B';
if ($bytes < 1048576) return round($bytes / 1024, 1) . ' KB';
if ($bytes < 1073741824) return round($bytes / 1048576, 1) . ' MB';
return round($bytes / 1073741824, 2) . ' GB';
}
// ── GET: List all saved queries ────────────────────────────────────────
if ($method === 'GET') {
try {
$rows = $pdo->query(
"SELECT id, name, follows_filter, kinds, from_date, to_date, max_events,\n"
. " last_preview_count, last_preview_size_bytes,\n"
. " last_executed_at, created_at, updated_at\n"
. " FROM cleanup_saved_queries\n"
. " ORDER BY updated_at DESC"
)->fetchAll();
} catch (PDOException $e) {
// Table may not exist yet
json_response(['queries' => []]);
exit;
}
$queries = [];
foreach ($rows as $r) {
$kinds = $r['kinds'];
if (is_string($kinds)) {
// PostgreSQL returns {1,7} format — parse it
$kinds = trim($kinds, '{}');
$kinds = $kinds !== '' ? array_map('intval', explode(',', $kinds)) : [];
} elseif (is_resource($kinds)) {
$kinds = [];
}
$queries[] = [
'id' => intval($r['id']),
'name' => $r['name'],
'follows_filter' => $r['follows_filter'],
'kinds' => $kinds,
'from_date' => $r['from_date'] ?? '',
'to_date' => $r['to_date'] ?? '',
'max_events' => intval($r['max_events']),
'last_preview_count' => intval($r['last_preview_count']),
'last_preview_size_human' => fmt_bytes(intval($r['last_preview_size_bytes'])),
'last_executed_at' => intval($r['last_executed_at']) > 0
? date('Y-m-d H:i:s', intval($r['last_executed_at']))
: null,
'created_at' => date('Y-m-d H:i:s', intval($r['created_at'])),
'updated_at' => date('Y-m-d H:i:s', intval($r['updated_at'])),
];
}
json_response(['queries' => $queries]);
exit;
}
// ── POST: Actions ──────────────────────────────────────────────────────
if ($method === 'POST') {
$body = json_decode(file_get_contents('php://input'), true);
if (!$body || empty($body['action'])) {
json_response(['error' => 'Missing action']);
exit;
}
$action = $body['action'];
// ── Save (create or update) ────────────────────────────────────────
if ($action === 'save') {
$id = $body['id'] ?? null;
$name = trim($body['name'] ?? '');
if ($name === '') {
json_response(['error' => 'Name is required']);
exit;
}
$follows_filter = $body['follows_filter'] ?? 'all';
if (!in_array($follows_filter, ['all', 'follows', 'non_follows'])) {
$follows_filter = 'all';
}
$kinds = $body['kinds'] ?? [];
if (is_array($kinds)) {
$kinds = array_map('intval', $kinds);
$kinds = array_filter($kinds, fn($v) => $v > 0);
$kinds = array_values($kinds);
} else {
$kinds = [];
}
// PostgreSQL array literal
$kinds_pg = '{' . implode(',', $kinds) . '}';
$from_date = $body['from_date'] ?? '';
$to_date = $body['to_date'] ?? '';
$max_events = intval($body['max_events'] ?? 0);
try {
if ($id) {
// Update existing
$stmt = $pdo->prepare(
"UPDATE cleanup_saved_queries\n"
. " SET name = :name,\n"
. " follows_filter = :follows_filter,\n"
. " kinds = :kinds::integer[],\n"
. " from_date = :from_date,\n"
. " to_date = :to_date,\n"
. " max_events = :max_events,\n"
. " updated_at = EXTRACT(EPOCH FROM NOW())::BIGINT\n"
. " WHERE id = :id"
);
$stmt->execute([
':id' => $id,
':name' => $name,
':follows_filter' => $follows_filter,
':kinds' => $kinds_pg,
':from_date' => $from_date,
':to_date' => $to_date,
':max_events' => $max_events,
]);
} else {
// Insert new
$stmt = $pdo->prepare(
"INSERT INTO cleanup_saved_queries\n"
. " (name, follows_filter, kinds, from_date, to_date, max_events)\n"
. "VALUES (:name, :follows_filter, :kinds::integer[],\n"
. " :from_date, :to_date, :max_events)\n"
. "ON CONFLICT (name) DO UPDATE SET\n"
. " follows_filter = EXCLUDED.follows_filter,\n"
. " kinds = EXCLUDED.kinds,\n"
. " from_date = EXCLUDED.from_date,\n"
. " to_date = EXCLUDED.to_date,\n"
. " max_events = EXCLUDED.max_events,\n"
. " updated_at = EXTRACT(EPOCH FROM NOW())::BIGINT"
);
$stmt->execute([
':name' => $name,
':follows_filter' => $follows_filter,
':kinds' => $kinds_pg,
':from_date' => $from_date,
':to_date' => $to_date,
':max_events' => $max_events,
]);
$id = $pdo->lastInsertId();
}
} catch (PDOException $e) {
json_response(['error' => 'Save failed: ' . $e->getMessage()]);
exit;
}
json_response(['ok' => true, 'id' => intval($id)]);
exit;
}
// ── Delete ─────────────────────────────────────────────────────────
if ($action === 'delete') {
$id = intval($body['id'] ?? 0);
if ($id <= 0) {
json_response(['error' => 'Invalid id']);
exit;
}
try {
$stmt = $pdo->prepare("DELETE FROM cleanup_saved_queries WHERE id = :id");
$stmt->execute([':id' => $id]);
} catch (PDOException $e) {
json_response(['error' => 'Delete failed: ' . $e->getMessage()]);
exit;
}
json_response(['ok' => true]);
exit;
}
// ── Execute (run a saved query as non-dry-run DELETE) ──────────────
if ($action === 'execute') {
$id = intval($body['id'] ?? 0);
if ($id <= 0) {
json_response(['error' => 'Invalid id']);
exit;
}
// Load the saved query
try {
$stmt = $pdo->prepare(
"SELECT id, name, follows_filter, kinds, from_date, to_date, max_events\n"
. " FROM cleanup_saved_queries\n"
. " WHERE id = :id"
);
$stmt->execute([':id' => $id]);
$query = $stmt->fetch();
} catch (PDOException $e) {
json_response(['error' => 'Query load failed: ' . $e->getMessage()]);
exit;
}
if (!$query) {
json_response(['error' => 'Saved query not found']);
exit;
}
// Parse kinds from PG array
$kinds_raw = $query['kinds'];
if (is_string($kinds_raw)) {
$kinds_raw = trim($kinds_raw, '{}');
$kinds = $kinds_raw !== '' ? array_map('intval', explode(',', $kinds_raw)) : [];
} else {
$kinds = [];
}
// Build filters (same logic as cleanup.php)
$conds = [];
$params = [];
if (!empty($kinds)) {
$kind_placeholders = [];
foreach ($kinds as $i => $k) {
$key = ':kind_' . $i;
$kind_placeholders[] = $key;
$params[$key] = $k;
}
$conds[] = 'e.kind IN (' . implode(',', $kind_placeholders) . ')';
}
$from_date = $query['from_date'] ?? '';
$to_date = $query['to_date'] ?? '';
if ($from_date !== '') {
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $from_date)) {
$conds[] = 'e.created_at >= EXTRACT(EPOCH FROM :from_date::date)::BIGINT';
$params[':from_date'] = $from_date;
} elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $from_date)) {
$conds[] = 'e.created_at >= EXTRACT(EPOCH FROM :from_date::timestamp)::BIGINT';
$params[':from_date'] = $from_date . ':00';
}
}
if ($to_date !== '') {
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $to_date)) {
$conds[] = 'e.created_at < (EXTRACT(EPOCH FROM :to_date::date)::BIGINT + 86400)';
$params[':to_date'] = $to_date;
} elseif (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $to_date)) {
$conds[] = 'e.created_at < EXTRACT(EPOCH FROM :to_date::timestamp)::BIGINT';
$params[':to_date'] = $to_date . ':00';
}
}
$follows_filter = $query['follows_filter'];
if ($follows_filter === 'follows') {
$conds[] = 'e.pubkey IN (SELECT pubkey FROM caching_followed_pubkeys)';
} elseif ($follows_filter === 'non_follows') {
$conds[] = 'e.pubkey NOT IN (SELECT pubkey FROM caching_followed_pubkeys)';
}
$where = !empty($conds) ? 'AND ' . implode("\n AND ", $conds) : '';
$max_events = intval($query['max_events']);
$limit_clause = $max_events > 0 ? 'LIMIT :max_events' : '';
// Pre-delete count
$preview_sql = "SELECT COUNT(*) AS match_count,\n"
. " COALESCE(SUM(pg_column_size(event_json)), 0) AS total_size_bytes\n"
. "FROM events e\n"
. "WHERE 1=1 $where";
try {
$stmt = $pdo->prepare($preview_sql);
$stmt->execute($params);
$row = $stmt->fetch();
$expected_count = intval($row['match_count'] ?? 0);
$expected_bytes = intval($row['total_size_bytes'] ?? 0);
} catch (PDOException $e) {
json_response(['error' => 'Pre-delete count failed: ' . $e->getMessage()]);
exit;
}
// Execute DELETE
$delete_sql = "DELETE FROM events e\n"
. "WHERE e.id IN (\n"
. " SELECT e2.id FROM events e2\n"
. " WHERE 1=1 $where\n"
. " ORDER BY e2.created_at ASC\n"
. " $limit_clause\n"
. ")";
$start_time = microtime(true);
try {
$stmt = $pdo->prepare($delete_sql);
if ($max_events > 0) {
$params[':max_events'] = $max_events;
}
$stmt->execute($params);
$deleted_count = $stmt->rowCount();
} catch (PDOException $e) {
json_response(['error' => 'Delete failed: ' . $e->getMessage()]);
exit;
}
$duration_ms = round((microtime(true) - $start_time) * 1000);
$freed_bytes = $expected_count > 0
? intval($expected_bytes * ($deleted_count / $expected_count))
: 0;
// Update last_preview_count, last_preview_size_bytes, last_executed_at
try {
$pdo->prepare(
"UPDATE cleanup_saved_queries\n"
. " SET last_preview_count = :count,\n"
. " last_preview_size_bytes = :size,\n"
. " last_executed_at = EXTRACT(EPOCH FROM NOW())::BIGINT\n"
. " WHERE id = :id"
)->execute([
':id' => $id,
':count' => $expected_count,
':size' => $expected_bytes,
]);
} catch (PDOException $e) {}
// Clear stats cache so the dashboard reflects changes immediately
$cache_dir = __DIR__ . '/../cache';
foreach (['stats_kinds.json', 'stats_pubkeys.json'] as $cache_file) {
$path = $cache_dir . '/' . $cache_file;
if (is_file($path)) @unlink($path);
}
json_response([
'deleted_count' => $deleted_count,
'freed_bytes' => $freed_bytes,
'freed_human' => fmt_bytes($freed_bytes),
'duration_ms' => $duration_ms,
]);
exit;
}
json_response(['error' => 'Unknown action: ' . $action]);
exit;
}
json_response(['error' => 'Method not allowed']);
+6 -2
View File
@@ -12,17 +12,21 @@ $params = [];
if ($kind !== '') { $where[] = 'kind = ?'; $params[] = intval($kind); }
$where_sql = $where ? 'WHERE ' . implode(' AND ', $where) : '';
$rows = $pdo->prepare("SELECT e.id, e.pubkey, e.kind, e.created_at, e.content,
// Order by first_seen DESC to show most recently received events first,
// avoiding corrupted created_at timestamps (e.g. far-future values) that
// would otherwise dominate the ordering.
$rows = $pdo->prepare("SELECT e.id, e.pubkey, e.kind, e.created_at, e.first_seen, e.content,
p.name, p.display_name
FROM events e
LEFT JOIN profiles p ON p.pubkey = e.pubkey
$where_sql
ORDER BY e.created_at DESC LIMIT $limit OFFSET $offset");
ORDER BY e.first_seen DESC LIMIT $limit OFFSET $offset");
$rows->execute($params);
$events = $rows->fetchAll();
foreach ($events as &$e) {
$e['created_at'] = date('Y-m-d H:i:s', intval($e['created_at']));
$e['first_seen'] = date('Y-m-d H:i:s', intval($e['first_seen']));
$e['content'] = substr($e['content'] ?? '', 0, 200);
// Resolve display name from profiles cache; fall back to truncated pubkey.
$best = profile_display_name($e);
+238
View File
@@ -0,0 +1,238 @@
<?php
/**
* admin/api/kind_1_report.php — Markdown relay status report.
*
* Returns a markdown-formatted relay status report suitable for use as
* a kind 1 event content. Includes:
* - Relay name header
* - 1H ASCII chart (event rate)
* - Database overview
* - Top 10 event kinds
* - Time-based statistics
* - Top pubkeys
*
* Accessible at: /relay/admin/api/kind_1_report.php
*/
require_once __DIR__ . '/../lib/helpers.php';
require_once __DIR__ . '/../lib/ascii_chart.php';
header('Content-Type: text/plain; charset=utf-8');
$pdo = db();
// --- Relay info ---
$relay_name = 'C-Relay-PG';
$relay_version = '';
try {
$relay_name = $pdo->query("SELECT value FROM config WHERE key = 'relay_name'")->fetchColumn() ?: 'C-Relay-PG';
$relay_version = $pdo->query("SELECT value FROM config WHERE key = 'relay_version'")->fetchColumn() ?: '';
} catch (PDOException $e) {}
$display_name = $relay_version ? "$relay_name v$relay_version" : $relay_name;
// --- Total events ---
$total_events = 0;
try {
$total_events = intval($pdo->query("SELECT COUNT(*) FROM events")->fetchColumn());
} catch (PDOException $e) {}
// --- Database size ---
$db_size = '-';
try {
$bytes = intval($pdo->query("SELECT pg_database_size(current_database())")->fetchColumn());
if ($bytes < 1073741824) {
$db_size = round($bytes / 1048576, 1) . ' MB';
} else {
$db_size = round($bytes / 1073741824, 2) . ' GB';
}
} catch (PDOException $e) {}
// --- Process ID ---
$process_id = '-';
try {
$pid = $pdo->query("SELECT pg_backend_pid()")->fetchColumn();
$process_id = strval($pid);
} catch (PDOException $e) {}
// --- WebSocket connections (estimate from pg_class) ---
$ws_connections = 0;
try {
$est = intval($pdo->query("SELECT reltuples FROM pg_class WHERE relname = 'subscriptions'")->fetchColumn());
$ws_connections = max(0, intval($est * 0.2));
} catch (PDOException $e) {}
// --- Active subscriptions (same estimate) ---
$active_subscriptions = $ws_connections;
// --- Memory usage ---
$memory_usage = '-';
$mem_total = 0;
$mem_avail = 0;
$meminfo = @file_get_contents('/proc/meminfo');
if ($meminfo !== false) {
// /proc/meminfo uses "Key: Value kB" format (colon, not equals sign)
foreach (explode("\n", $meminfo) as $line) {
if (preg_match('/^MemTotal:\s+(\d+)\s+kB/i', $line, $m)) {
$mem_total = intval($m[1]) * 1024;
} elseif (preg_match('/^MemAvailable:\s+(\d+)\s+kB/i', $line, $m)) {
$mem_avail = intval($m[1]) * 1024;
}
}
}
if ($mem_total > 0) {
$used = $mem_total - $mem_avail;
if ($used < 1073741824) {
$memory_usage = round($used / 1048576, 1) . ' MB / ' . round($mem_total / 1073741824, 2) . ' GB';
} else {
$memory_usage = round($used / 1073741824, 2) . ' GB / ' . round($mem_total / 1073741824, 2) . ' GB';
}
}
// --- CPU ---
$cpu_usage = '-';
$cpu_core = '-';
if (is_readable('/proc/cpuinfo')) {
$cores = intval(@shell_exec('nproc 2>/dev/null') ?: 1);
$cpu_core = strval($cores) . ' cores';
}
$load = @file_get_contents('/proc/loadavg');
if ($load !== false) {
$cpu_usage = trim(explode(' ', $load)[0] ?? '-');
}
// --- Oldest / newest event ---
$oldest_event = '-';
$newest_event = '-';
try {
$row = $pdo->query("SELECT MIN(created_at) AS oldest, MAX(created_at) AS newest FROM events")->fetch();
if ($row && $row['oldest']) $oldest_event = date('Y-m-d H:i:s', intval($row['oldest']));
if ($row && $row['newest']) $newest_event = date('Y-m-d H:i:s', intval($row['newest']));
} catch (PDOException $e) {}
// --- Time-based stats ---
$now = time();
$events_24h = 0; $events_7d = 0; $events_30d = 0;
try {
$events_24h = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 86400")->fetchColumn());
$events_7d = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 604800")->fetchColumn());
$events_30d = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 2592000")->fetchColumn());
} catch (PDOException $e) {}
// --- Kind distribution (top 10) ---
$kinds = [];
try {
$rows = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 10")->fetchAll();
foreach ($rows as $r) {
$kinds[] = ['kind' => intval($r['kind']), 'count' => intval($r['cnt']), 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0];
}
} catch (PDOException $e) {}
// --- Top pubkeys (top 20) ---
$top_pubkeys = [];
try {
$rows = $pdo->query("
SELECT pubkey, COUNT(*) AS cnt
FROM events
GROUP BY pubkey
ORDER BY cnt DESC LIMIT 20
")->fetchAll();
$pubkeys = array_column($rows, 'pubkey');
$pmap = profile_map($pubkeys);
foreach ($rows as $r) {
$pk = $r['pubkey'];
$prof = $pmap[$pk] ?? null;
$top_pubkeys[] = [
'pubkey' => $pk,
'name' => $prof ? $prof['best_name'] : '',
'count' => intval($r['cnt']),
'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0,
];
}
} catch (PDOException $e) {}
// --- 1H ASCII chart (30 bins of 2 minutes each) ---
$chart = '';
try {
$span = 3600;
$bin_size = 120; // 2 minutes
$num_bins = 30;
$epoch = $now - $span;
$base_bin = (int)floor($epoch / $bin_size);
$sql = "SELECT FLOOR(created_at / {$bin_size})::BIGINT - {$base_bin} AS bin, COUNT(*) AS cnt
FROM events
WHERE created_at >= {$epoch}
GROUP BY bin
ORDER BY bin";
$rows = $pdo->query($sql)->fetchAll();
$bins = build_bin_array($rows, $num_bins);
$bins = array_reverse($bins);
$chart = render_ascii_chart($bins, [
'title' => 'Event Rate (Last Hour)',
'max_height' => 11,
'bin_duration' => $bin_size,
'label_interval' => 5, // label every 5 bins (10 min)
]);
} catch (PDOException $e) {
$chart = "Chart unavailable\n";
}
// ================================
// BUILD MARKDOWN OUTPUT
// ================================
$output = '';
// Chart
$output .= "## laantungir.net/relay\n\n";
$output .= "```\n$chart```\n";
// Database Overview
$output .= "## Database Overview\n\n";
$output .= "| Metric | Value |\n";
$output .= "|--------|-------|\n";
$output .= "| Database Size | $db_size |\n";
$output .= "| Total Events | " . number_format($total_events) . " |\n";
$output .= "| Process ID | $process_id |\n";
$output .= "| WebSocket Connections | " . number_format($ws_connections) . " |\n";
$output .= "| Active Subscriptions | " . number_format($active_subscriptions) . " |\n";
$output .= "| Memory Usage | $memory_usage |\n";
$output .= "| CPU Usage | $cpu_usage |\n";
$output .= "| CPU Core | $cpu_core |\n";
$output .= "| Oldest Event | $oldest_event |\n";
$output .= "| Newest Event | $newest_event |\n\n";
// Event Kinds (Top 10)
$output .= "## Event Kinds (Top 10)\n\n";
$output .= "| Kind | Count | % |\n";
$output .= "|------|-------|---|\n";
foreach ($kinds as $k) {
$output .= "| {$k['kind']} | " . number_format($k['count']) . " | {$k['pct']}% |\n";
}
$output .= "\n";
// Time-Based Statistics
$output .= "## Time-Based Statistics\n\n";
$output .= "| Period | Events |\n";
$output .= "|--------|-------|\n";
$output .= "| 24 Hours | " . number_format($events_24h) . " |\n";
$output .= "| 7 Days | " . number_format($events_7d) . " |\n";
$output .= "| 30 Days | " . number_format($events_30d) . " |\n\n";
// Top Pubkeys
$output .= "## Top Pubkeys\n\n";
$output .= "| Name | Events | % |\n";
$output .= "|------|-------|---|\n";
foreach ($top_pubkeys as $p) {
if ($p['name']) {
$name = $p['name'];
} else {
$name = substr($p['pubkey'], 0, 4) . '..' . substr($p['pubkey'], -4);
}
$output .= "| $name | " . number_format($p['count']) . " | {$p['pct']}% |\n";
}
$output .= "\n";
// Footer
$output .= "---\n";
$output .= "_Report generated: " . date('Y-m-d H:i:s T') . "_\n";
echo $output;
+80 -46
View File
@@ -24,46 +24,66 @@ try {
$events_delta = intval($pdo->query("SELECT COUNT(*) FROM events WHERE first_seen >= EXTRACT(EPOCH FROM NOW())::BIGINT - 10")->fetchColumn());
} catch (PDOException $e) {}
// Process info (from pg_stat_activity)
// Process info
$process_id = '-';
$ws_connections = '-';
try {
$pid = $pdo->query("SELECT pg_backend_pid()")->fetchColumn();
$process_id = strval($pid);
$ws_connections = intval($pdo->query("SELECT count(*) FROM pg_stat_activity WHERE state = 'active' AND pid != pg_backend_pid()")->fetchColumn());
} catch (PDOException $e) {}
// Active subscriptions
$active_subscriptions = 0;
// WebSocket connections: tracked in-memory by the relay (g_connection_count).
// The subscriptions table is a 32M-row historical log, not a live state table.
// Use pg_class for an instant estimate (updated by autovacuum).
$ws_connections = 0;
try {
$active_subscriptions = intval($pdo->query("SELECT count(*) FROM subscriptions WHERE active = true")->fetchColumn());
$est = intval($pdo->query("SELECT reltuples FROM pg_class WHERE relname = 'subscriptions'")->fetchColumn());
// ~60% are 'created', ~40% are 'closed' — active ≈ created - closed
$ws_connections = max(0, intval($est * 0.2));
} catch (PDOException $e) {}
// Active subscriptions: same estimate approach.
$active_subscriptions = $ws_connections;
// Memory/CPU (from /proc on Linux)
$memory_usage = '-';
$cpu_usage = '-';
$cpu_core = '-';
if (is_readable('/proc/meminfo')) {
$mem = parse_ini_file('/proc/meminfo');
$total = intval($mem['MemTotal'] ?? 0) * 1024;
$avail = intval($mem['MemAvailable'] ?? 0) * 1024;
if ($total > 0) $memory_usage = format_bytes($total - $avail) . ' / ' . format_bytes($total);
$mem_total = 0;
$mem_avail = 0;
$meminfo = @file_get_contents('/proc/meminfo');
if ($meminfo !== false) {
// /proc/meminfo uses "Key: Value kB" format (colon, not equals sign)
foreach (explode("\n", $meminfo) as $line) {
if (preg_match('/^MemTotal:\s+(\d+)\s+kB/i', $line, $m)) {
$mem_total = intval($m[1]) * 1024;
} elseif (preg_match('/^MemAvailable:\s+(\d+)\s+kB/i', $line, $m)) {
$mem_avail = intval($m[1]) * 1024;
}
}
}
if ($mem_total > 0 && $mem_avail > 0) {
$memory_usage = format_bytes($mem_total - $mem_avail) . ' / ' . format_bytes($mem_total);
} elseif ($mem_total > 0) {
$memory_usage = format_bytes($mem_total);
}
if (is_readable('/proc/cpuinfo')) {
$cores = intval(shell_exec('nproc 2>/dev/null') ?: 1);
$cores = intval(@shell_exec('nproc 2>/dev/null') ?: 1);
$cpu_core = strval($cores) . ' cores';
}
$cpu_usage = @file_get_contents('/proc/loadavg');
if ($cpu_usage !== false) $cpu_usage = trim(explode(' ', $cpu_usage)[0] ?? '-');
// Oldest / newest event
// Oldest / newest event (single query)
$oldest_event = '-';
$newest_event = '-';
try {
$oldest = $pdo->query("SELECT to_timestamp(MIN(created_at)) FROM events")->fetchColumn();
$newest = $pdo->query("SELECT to_timestamp(MAX(created_at)) FROM events")->fetchColumn();
if ($oldest) $oldest_event = substr($oldest, 0, 19);
if ($newest) $newest_event = substr($newest, 0, 19);
$row = $pdo->query("SELECT MIN(created_at) AS oldest, MAX(created_at) AS newest FROM events")->fetch();
if ($row && $row['oldest']) {
$oldest_event = date('Y-m-d H:i:s', intval($row['oldest']));
}
if ($row && $row['newest']) {
$newest_event = date('Y-m-d H:i:s', intval($row['newest']));
}
} catch (PDOException $e) {}
// Time-based stats
@@ -75,38 +95,52 @@ try {
$events_30d = intval($pdo->query("SELECT COUNT(*) FROM events WHERE created_at >= $now - 2592000")->fetchColumn());
} catch (PDOException $e) {}
// Kind distribution
// Kind distribution — cached to file, refreshed every 5 minutes
$kinds = [];
try {
$rows = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 20")->fetchAll();
foreach ($rows as $r) {
$kinds[] = ['kind' => intval($r['kind']), 'count' => intval($r['cnt']), 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0];
}
} catch (PDOException $e) {}
$cache_dir = __DIR__ . '/../cache';
if (!is_dir($cache_dir)) @mkdir($cache_dir, 0755, true);
$kinds_cache = $cache_dir . '/stats_kinds.json';
$kinds_ttl = 300;
if (is_readable($kinds_cache) && (time() - filemtime($kinds_cache)) < $kinds_ttl) {
$kinds = json_decode(file_get_contents($kinds_cache), true) ?: [];
} else {
try {
$rows = $pdo->query("SELECT kind, COUNT(*) AS cnt FROM events GROUP BY kind ORDER BY cnt DESC LIMIT 20")->fetchAll();
foreach ($rows as $r) {
$kinds[] = ['kind' => intval($r['kind']), 'count' => intval($r['cnt']), 'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0];
}
@file_put_contents($kinds_cache, json_encode($kinds));
} catch (PDOException $e) {}
}
// Top pubkeys (with names from profiles cache)
// Top pubkeys — cached to file, refreshed every 5 minutes
$top_pubkeys = [];
try {
$rows = $pdo->query("
SELECT pubkey, COUNT(*) AS cnt
FROM events
GROUP BY pubkey
ORDER BY cnt DESC LIMIT 20
")->fetchAll();
// Batch-resolve profile names from the profiles cache table.
$pubkeys = array_column($rows, 'pubkey');
$pmap = profile_map($pubkeys);
foreach ($rows as $r) {
$pk = $r['pubkey'];
$prof = $pmap[$pk] ?? null;
$top_pubkeys[] = [
'pubkey' => $pk,
'name' => $prof ? $prof['best_name'] : '',
'count' => intval($r['cnt']),
'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0,
];
}
} catch (PDOException $e) {}
$pubkeys_cache = $cache_dir . '/stats_pubkeys.json';
if (is_readable($pubkeys_cache) && (time() - filemtime($pubkeys_cache)) < $kinds_ttl) {
$top_pubkeys = json_decode(file_get_contents($pubkeys_cache), true) ?: [];
} else {
try {
$rows = $pdo->query("
SELECT pubkey, COUNT(*) AS cnt
FROM events
GROUP BY pubkey
ORDER BY cnt DESC LIMIT 20
")->fetchAll();
$pubkeys = array_column($rows, 'pubkey');
$pmap = profile_map($pubkeys);
foreach ($rows as $r) {
$pk = $r['pubkey'];
$prof = $pmap[$pk] ?? null;
$top_pubkeys[] = [
'pubkey' => $pk,
'name' => $prof ? $prof['best_name'] : '',
'count' => intval($r['cnt']),
'pct' => $total_events > 0 ? round(intval($r['cnt']) / $total_events * 100, 1) : 0,
];
}
@file_put_contents($pubkeys_cache, json_encode($top_pubkeys));
} catch (PDOException $e) {}
}
// Name-field usage stats (from profiles cache)
$name_field_usage = ['both' => 0, 'name_only' => 0, 'display_only' => 0, 'neither' => 0, 'both_differ' => 0, 'total' => 0];
+408 -7
View File
@@ -59,7 +59,7 @@ function switchPage(pageName) {
const sections = [
'databaseStatisticsSection', 'subscriptionDetailsSection', 'div_config',
'authRulesSection', 'wotSection', 'ipBansSection', 'relayEventsSection',
'cachingSection', 'nip17DMSection', 'sqlQuerySection'
'cachingSection', 'nip17DMSection', 'cleanupSection', 'sqlQuerySection'
];
sections.forEach(id => { const el = document.getElementById(id); if (el) el.style.display = 'none'; });
@@ -71,6 +71,7 @@ function switchPage(pageName) {
'relay-events': 'relayEventsSection',
'caching': 'cachingSection',
'dm': 'nip17DMSection',
'cleanup': 'cleanupSection',
'database': 'sqlQuerySection'
};
@@ -93,14 +94,21 @@ function switchPage(pageName) {
'relay-events': loadEvents,
'caching': loadCaching,
'dm': loadDMs,
'cleanup': loadCleanupQueries,
};
if (loaders[pageName]) loaders[pageName]();
// Start/stop auto-refresh for statistics
if (pageName === 'statistics') {
// Auto-refresh: one interval that calls the right loader for the current page.
// When switching between statistics and caching, the interval keeps running
// but picks the correct loader based on currentPage.
if (pageName === 'statistics' || pageName === 'caching') {
const loader = pageName === 'statistics' ? loadStats : loadCaching;
loader(); // always load immediately on switch
if (!statsInterval) {
loadStats();
statsInterval = setInterval(loadStats, REFRESH_MS);
statsInterval = setInterval(() => {
const l = currentPage === 'statistics' ? loadStats : loadCaching;
l();
}, REFRESH_MS);
console.log('[admin2] auto-refresh started, interval:', REFRESH_MS, 'ms');
}
} else {
@@ -412,9 +420,11 @@ async function loadEvents() {
// CACHING
// ================================
let cachingPage = 1;
async function loadCaching() {
try {
const res = await fetch('api/caching.php');
const res = await fetch('api/caching.php?page=' + cachingPage);
const d = await res.json();
// Config toggle state
if (d.config) {
@@ -441,14 +451,39 @@ async function loadCaching() {
if (isEl && d.inbox) {
isEl.innerHTML = `<p>Pending: ${d.inbox.pending} | Live: ${d.inbox.live} | Backfill: ${d.inbox.backfill}</p>`;
}
// Upstream relay status window
const rsEl = document.getElementById('caching-relay-status');
if (rsEl) {
if (d.upstreamRelays && d.upstreamRelays.length > 0) {
const connected = d.upstreamRelays.filter(r => r.status_code == 2).length;
const total = d.upstreamRelays.length;
rsEl.innerHTML = `<div class="relay-status-summary">Connected: ${connected}/${total}</div>` +
'<div class="relay-status-list">' +
d.upstreamRelays.map(r => {
const host = r.relay_url.replace(/^wss?:\/\//, '').replace(/\/relay$/, '');
let cls = 'relay-status-unknown';
let label = esc(r.status_text || 'unknown');
if (r.status_code == 2) { cls = 'relay-status-ok'; label = 'connected'; }
else if (r.status_code == 1) { cls = 'relay-status-connecting'; label = 'connecting'; }
else if (r.status_code == 0) { cls = 'relay-status-disconnected'; label = 'disconnected'; }
else if (r.status_code < 0) { cls = 'relay-status-error'; label = esc(r.status_text || 'error'); }
return `<div class="relay-status-row ${cls}"><span class="relay-status-url" title="${esc(r.relay_url)}">${esc(host)}</span><span class="relay-status-badge">${label}</span></div>`;
}).join('') +
'</div>';
} else {
rsEl.innerHTML = '<p style="color:var(--muted-color);font-style:italic">No relay status data yet (waiting for heartbeat)</p>';
}
}
// Follows table
const tbody = document.getElementById('caching-follows-table-body');
if (tbody && d.follows) {
if (d.follows.length === 0) {
tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;font-style:italic">No followed pubkeys</td></tr>';
} else {
const activePk = d.active && d.active.pubkey ? d.active.pubkey : '';
tbody.innerHTML = d.follows.map((f, i) => {
const npub = f.npub || f.pubkey.substring(0, 20);
const isActive = activePk && f.pubkey === activePk;
const relaySummary = f.relay_incomplete > 0
? `<span class="status-working">${f.relay_count - f.relay_incomplete}/${f.relay_count} done</span>`
: `${f.relay_count ?? 0} relays`;
@@ -467,10 +502,24 @@ async function loadCaching() {
'</div>';
}
const refreshBtn = `<button type="button" class="refresh-user-btn" onclick="event.stopPropagation(); refreshCachingUser('${esc(f.pubkey)}', '${esc(f.name || '')}')">↻ Refresh this user</button>`;
return `<tr class="follows-row" onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? '' : 'none'"><td><bdi>${esc(f.name) || '<i>unknown</i>'}</bdi></td><td class="npub-link">${esc(npub)}…</td><td>${f.is_root ? '✓' : ''}</td><td>${f.total_events ?? 0}</td><td>${f.backfill_complete ? '✓' : '…'}</td><td>${relaySummary}</td></tr><tr class="follows-detail" style="display:none"><td colspan="6"><div class="follows-detail-content">${relayDetail || '<i>No relay progress data</i>'}<div class="follows-detail-actions">${refreshBtn}</div></div></td></tr>`;
const rowClass = isActive ? 'follows-row follows-row-active' : 'follows-row';
const activeIcon = isActive ? '⚡ ' : '';
return `<tr class="${rowClass}" onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? '' : 'none'"><td><bdi>${activeIcon}${esc(f.name) || '<i>unknown</i>'}</bdi></td><td class="npub-link">${esc(npub)}…</td><td>${f.is_root ? '✓' : ''}</td><td>${f.total_events ?? 0}</td><td>${f.backfill_complete ? '✓' : '…'}</td><td>${relaySummary}</td></tr><tr class="follows-detail" style="display:none"><td colspan="6"><div class="follows-detail-content">${relayDetail || '<i>No relay progress data</i>'}<div class="follows-detail-actions">${refreshBtn}</div></div></td></tr>`;
}).join('');
}
}
// Pagination controls
const pgEl = document.getElementById('caching-follows-pagination');
if (pgEl && d.totalFollows > 0) {
const totalPages = Math.ceil(d.totalFollows / (d.perPage || 50));
pgEl.innerHTML = '<span class="pagination-info">Page ' + d.page + ' of ' + totalPages + ' (' + d.totalFollows + ' total)</span>' +
'<div class="pagination-buttons">' +
(d.page > 1 ? '<button type="button" onclick="cachingPage=' + (d.page - 1) + '; loadCaching()"> Prev</button>' : '') +
(d.page < totalPages ? '<button type="button" onclick="cachingPage=' + (d.page + 1) + '; loadCaching()">Next </button>' : '') +
'</div>';
} else if (pgEl) {
pgEl.innerHTML = '';
}
// Active target
const fsEl = document.getElementById('caching-follows-status');
if (fsEl) {
@@ -630,6 +679,358 @@ async function executeQuery() {
} catch (e) { console.error('[admin2] query error:', e); }
}
// ================================
// EVENT CLEANUP
// ================================
// ── State ──────────────────────────────────────────────────────────────
let cleanupEditId = null; // non-null when editing an existing saved query
// ── Load saved queries table ───────────────────────────────────────────
async function loadCleanupQueries() {
console.log('[admin2] loading cleanup queries');
try {
const res = await fetch('api/cleanup_queries.php');
const d = await res.json();
const tbody = document.getElementById('cleanup-saved-queries-body');
if (!d.queries || d.queries.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;font-style:italic">No saved queries. Create one below.</td></tr>';
return;
}
tbody.innerHTML = d.queries.map(q => {
const filterLabel = q.follows_filter === 'all' ? 'All'
: q.follows_filter === 'follows' ? 'Follows'
: 'Non-follows';
const kindsStr = q.kinds && q.kinds.length > 0 ? q.kinds.join(', ') : 'all';
const dateStr = (q.from_date || q.to_date)
? `${q.from_date || '…'}${q.to_date || '…'}`
: 'no date bound';
const limitStr = q.max_events > 0 ? q.max_events.toLocaleString() : '∞';
const filters = `${filterLabel} | kinds: ${kindsStr} | ${dateStr} | max: ${limitStr}`;
const previewInfo = q.last_preview_count > 0
? `${q.last_preview_count.toLocaleString()} evts (${q.last_preview_size_human})`
: '—';
const executedInfo = q.last_executed_at || '—';
return `<tr>
<td><strong>${esc(q.name)}</strong></td>
<td style="font-size:11px">${esc(filters)}</td>
<td style="font-size:11px">${previewInfo}</td>
<td style="font-size:11px">${esc(executedInfo)}</td>
<td>
<button onclick="runSavedCleanupPreview(${q.id})">Preview</button>
<button onclick="runSavedCleanupExecute(${q.id})">Execute</button>
<button onclick="loadSavedCleanupQuery(${q.id})">Edit</button>
<button onclick="deleteCleanupQuery(${q.id})">Delete</button>
</td>
</tr>`;
}).join('');
} catch (e) { console.error('[admin2] loadCleanupQueries error:', e); }
}
// ── New query (clear builder) ──────────────────────────────────────────
function newCleanupQuery() {
cleanupEditId = null;
document.getElementById('cleanup-name').value = '';
document.querySelectorAll('input[name="cleanup-follows"]').forEach(r => {
r.checked = r.value === 'all';
});
document.getElementById('cleanup-kinds').value = '';
document.getElementById('cleanup-from-date').value = '';
document.getElementById('cleanup-from-time').value = '';
document.getElementById('cleanup-to-date').value = '';
document.getElementById('cleanup-to-time').value = '';
document.getElementById('cleanup-max-events').value = '0';
document.getElementById('cleanup-results-group').style.display = 'none';
document.getElementById('cleanup-name').focus();
}
// ── Combine date + time inputs into a single datetime string ───────────
function combineDateTime(dateId, timeId) {
const dateVal = document.getElementById(dateId).value;
const timeVal = document.getElementById(timeId).value;
if (!dateVal) return '';
return timeVal ? dateVal + ' ' + timeVal : dateVal;
}
// ── Read filter values from the form ───────────────────────────────────
function readCleanupFilters() {
const followsEl = document.querySelector('input[name="cleanup-follows"]:checked');
const follows_filter = followsEl ? followsEl.value : 'all';
const kindsRaw = document.getElementById('cleanup-kinds').value.trim();
const kinds = kindsRaw ? kindsRaw.split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n) && n > 0) : [];
const from_date = combineDateTime('cleanup-from-date', 'cleanup-from-time');
const to_date = combineDateTime('cleanup-to-date', 'cleanup-to-time');
const max_events = parseInt(document.getElementById('cleanup-max-events').value, 10) || 0;
const name = document.getElementById('cleanup-name').value.trim();
return { name, follows_filter, kinds, from_date, to_date, max_events };
}
// ── Preview ────────────────────────────────────────────────────────────
async function previewCleanup(queryId) {
const filters = readCleanupFilters();
const params = new URLSearchParams();
params.set('follows_filter', filters.follows_filter);
if (filters.kinds.length > 0) params.set('kinds', filters.kinds.join(','));
if (filters.from_date) params.set('from_date', filters.from_date);
if (filters.to_date) params.set('to_date', filters.to_date);
if (filters.max_events > 0) params.set('max_events', String(filters.max_events));
if (queryId) params.set('query_id', String(queryId));
try {
const res = await fetch('api/cleanup.php?' + params.toString());
const d = await res.json();
if (d.error) { alert('Preview error: ' + d.error); return; }
renderCleanupResults(d);
// Refresh saved queries table so last_preview updates show
if (queryId) loadCleanupQueries();
} catch (e) { console.error('[admin2] previewCleanup error:', e); alert('Preview failed: ' + e.message); }
}
// ── Render preview results ─────────────────────────────────────────────
function renderCleanupResults(d) {
const group = document.getElementById('cleanup-results-group');
group.style.display = 'block';
// Summary
const summary = document.getElementById('cleanup-results-summary');
let html = `<strong>Match count:</strong> ${(d.match_count ?? 0).toLocaleString()} events`;
if (d.total_size_human) {
html += ` &nbsp;|&nbsp; <strong>Estimated size:</strong> ${d.total_size_human}`;
}
if (d.avg_size_per_event) {
html += ` &nbsp;|&nbsp; <strong>Avg/event:</strong> ${d.avg_size_per_event} bytes`;
}
if (d.deleted_count !== undefined) {
html += `<br><strong style="color:#c0392b">Deleted:</strong> ${d.deleted_count.toLocaleString()} events`;
if (d.freed_human) html += ` &nbsp;|&nbsp; <strong>Freed:</strong> ${d.freed_human}`;
if (d.duration_ms) html += ` &nbsp;|&nbsp; <strong>Duration:</strong> ${d.duration_ms}ms`;
}
summary.innerHTML = html;
// Kind breakdown table
const bdEl = document.getElementById('cleanup-results-breakdown');
if (d.kinds_breakdown && d.kinds_breakdown.length > 0) {
let tbl = '<table class="config-table"><thead><tr><th>Kind</th><th>Count</th><th>Size</th><th>% of total</th></tr></thead><tbody>';
const totalBytes = d.total_size_bytes || 1;
d.kinds_breakdown.forEach(k => {
const pct = totalBytes > 0 ? (k.size_bytes / totalBytes * 100).toFixed(1) : 0;
tbl += `<tr><td>${k.kind}</td><td>${k.count.toLocaleString()}</td><td>${formatCleanupBytes(k.size_bytes)}</td><td>${pct}%</td></tr>`;
});
tbl += '</tbody></table>';
bdEl.innerHTML = tbl;
} else {
bdEl.innerHTML = '<p style="font-style:italic;color:var(--text-muted,#888)">No kind breakdown available.</p>';
}
// SQL preview
const sqlEl = document.getElementById('cleanup-sql-preview');
if (d.sql_preview) {
sqlEl.textContent = d.sql_preview;
} else {
sqlEl.textContent = '(SQL preview not available for this response)';
}
}
// ── Format bytes (local helper) ────────────────────────────────────────
function formatCleanupBytes(bytes) {
if (bytes < 1024) return bytes + ' B';
if (bytes < 1048576) return (bytes / 1024).toFixed(1) + ' KB';
if (bytes < 1073741824) return (bytes / 1048576).toFixed(1) + ' MB';
return (bytes / 1073741824).toFixed(2) + ' GB';
}
// ── Save query ─────────────────────────────────────────────────────────
async function saveCleanupQuery() {
const filters = readCleanupFilters();
if (!filters.name) { alert('Please enter a query name'); return; }
const body = {
action: 'save',
id: cleanupEditId,
name: filters.name,
follows_filter: filters.follows_filter,
kinds: filters.kinds,
from_date: filters.from_date,
to_date: filters.to_date,
max_events: filters.max_events,
};
try {
const res = await fetch('api/cleanup_queries.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
});
const d = await res.json();
if (d.error) { alert('Save failed: ' + d.error); return; }
cleanupEditId = d.id;
alert('Query saved!');
loadCleanupQueries();
} catch (e) { console.error('[admin2] saveCleanupQuery error:', e); alert('Save failed: ' + e.message); }
}
// ── Execute delete (show confirmation first) ───────────────────────────
let pendingExecuteFilters = null;
async function executeCleanup() {
const filters = readCleanupFilters();
pendingExecuteFilters = filters;
// Run a quick preview to show the user what will be deleted
const params = new URLSearchParams();
params.set('follows_filter', filters.follows_filter);
if (filters.kinds.length > 0) params.set('kinds', filters.kinds.join(','));
if (filters.from_date) params.set('from_date', filters.from_date);
if (filters.to_date) params.set('to_date', filters.to_date);
if (filters.max_events > 0) params.set('max_events', String(filters.max_events));
try {
const res = await fetch('api/cleanup.php?' + params.toString());
const d = await res.json();
const name = filters.name || 'Ad-hoc query';
const msg = `Query: ${esc(name)}\n`
+ `Events to delete: ${(d.match_count ?? 0).toLocaleString()}\n`
+ `Estimated space freed: ${d.total_size_human || 'unknown'}`;
document.getElementById('cleanup-confirm-msg').textContent = msg;
document.getElementById('cleanup-confirm-dialog').style.display = 'block';
} catch (e) {
// If preview fails, still allow delete with a basic confirmation
document.getElementById('cleanup-confirm-msg').textContent = `Events matching current filters.`;
document.getElementById('cleanup-confirm-dialog').style.display = 'block';
}
}
async function executeCleanupConfirmed() {
document.getElementById('cleanup-confirm-dialog').style.display = 'none';
if (!pendingExecuteFilters) return;
const filters = pendingExecuteFilters;
pendingExecuteFilters = null;
const body = {
follows_filter: filters.follows_filter,
kinds: filters.kinds,
from_date: filters.from_date,
to_date: filters.to_date,
max_events: filters.max_events,
dry_run: false,
};
try {
const res = await fetch('api/cleanup.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(body)
});
const d = await res.json();
if (d.error) { alert('Delete failed: ' + d.error); return; }
renderCleanupResults(d);
// Refresh saved queries in case last_preview_count changed
loadCleanupQueries();
} catch (e) { console.error('[admin2] executeCleanup error:', e); alert('Delete failed: ' + e.message); }
}
// ── Delete saved query ─────────────────────────────────────────────────
async function deleteCleanupQuery(id) {
if (!confirm('Delete this saved query?')) return;
try {
const res = await fetch('api/cleanup_queries.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({action: 'delete', id})
});
const d = await res.json();
if (d.error) { alert('Delete failed: ' + d.error); return; }
loadCleanupQueries();
} catch (e) { console.error('[admin2] deleteCleanupQuery error:', e); alert('Delete failed: ' + e.message); }
}
// ── Load saved query into builder ──────────────────────────────────────
async function loadSavedCleanupQuery(id) {
try {
const res = await fetch('api/cleanup_queries.php');
const d = await res.json();
if (!d.queries) return;
const q = d.queries.find(q => q.id === id);
if (!q) { alert('Query not found'); return; }
cleanupEditId = q.id;
document.getElementById('cleanup-name').value = q.name;
document.querySelectorAll('input[name="cleanup-follows"]').forEach(r => {
r.checked = r.value === q.follows_filter;
});
document.getElementById('cleanup-kinds').value = (q.kinds || []).join(', ');
// Split stored datetime into date + time parts
const fromParts = (q.from_date || '').split(' ');
document.getElementById('cleanup-from-date').value = fromParts[0] || '';
document.getElementById('cleanup-from-time').value = fromParts[1] || '';
const toParts = (q.to_date || '').split(' ');
document.getElementById('cleanup-to-date').value = toParts[0] || '';
document.getElementById('cleanup-to-time').value = toParts[1] || '';
document.getElementById('cleanup-max-events').value = q.max_events;
document.getElementById('cleanup-results-group').style.display = 'none';
// Scroll to builder
document.getElementById('cleanup-builder-group').scrollIntoView({ behavior: 'smooth' });
} catch (e) { console.error('[admin2] loadSavedCleanupQuery error:', e); }
}
// ── Run preview for a saved query ──────────────────────────────────────
async function runSavedCleanupPreview(id) {
try {
const res = await fetch('api/cleanup_queries.php');
const d = await res.json();
if (!d.queries) return;
const q = d.queries.find(q => q.id === id);
if (!q) { alert('Query not found'); return; }
// Load into builder first so the user can see the filters
await loadSavedCleanupQuery(id);
// Then run preview with query_id so last_preview_count/size get saved
await previewCleanup(id);
} catch (e) { console.error('[admin2] runSavedCleanupPreview error:', e); }
}
// ── Run execute for a saved query ──────────────────────────────────────
async function runSavedCleanupExecute(id) {
try {
const res = await fetch('api/cleanup_queries.php');
const d = await res.json();
if (!d.queries) return;
const q = d.queries.find(q => q.id === id);
if (!q) { alert('Query not found'); return; }
// Load into builder
await loadSavedCleanupQuery(id);
// Confirm and execute via the saved query execute action
const filters = readCleanupFilters();
const name = q.name || 'Saved query';
const msg = `Query: ${name}\n`
+ `Follows: ${q.follows_filter}\n`
+ `Kinds: ${(q.kinds || []).join(', ') || 'all'}\n`
+ `From: ${q.from_date || 'no bound'}\n`
+ `To: ${q.to_date || 'no bound'}\n`
+ `Max events: ${q.max_events > 0 ? q.max_events.toLocaleString() : 'no limit'}`;
document.getElementById('cleanup-confirm-msg').textContent = msg;
document.getElementById('cleanup-confirm-btn').onclick = async () => {
document.getElementById('cleanup-confirm-dialog').style.display = 'none';
try {
const execRes = await fetch('api/cleanup_queries.php', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({action: 'execute', id})
});
const execD = await execRes.json();
if (execD.error) { alert('Delete failed: ' + execD.error); return; }
renderCleanupResults(execD);
loadCleanupQueries();
} catch (e) { console.error('[admin2] runSavedCleanupExecute error:', e); alert('Delete failed: ' + e.message); }
};
document.getElementById('cleanup-confirm-dialog').style.display = 'block';
} catch (e) { console.error('[admin2] runSavedCleanupExecute error:', e); }
}
// ================================
// DARK MODE
// ================================
+209
View File
@@ -281,6 +281,94 @@ body {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
/* Cleanup Date/Time Inputs */
.datetime-group {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
}
.cleanup-date-input {
font-family: var(--font-family);
font-size: 13px;
padding: 4px 6px;
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
background: var(--secondary-color);
color: var(--primary-color);
width: 160px;
}
.cleanup-time-input {
font-family: var(--font-family);
font-size: 13px;
padding: 4px 6px;
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
background: var(--secondary-color);
color: var(--primary-color);
width: 110px;
}
.datetime-hint {
font-size: 11px;
color: var(--muted-color);
font-style: italic;
}
/* Cleanup Confirmation Modal */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
z-index: 1001;
}
.modal-content {
background-color: var(--secondary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
}
/* Cleanup results summary */
.info-box {
padding: 8px 12px;
background: var(--bg-code, #f5f5f5);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
font-size: 13px;
line-height: 1.6;
}
/* Cleanup radio group */
.radio-group {
display: flex;
gap: 16px;
flex-wrap: wrap;
margin-top: 4px;
}
.radio-group label {
display: flex;
align-items: center;
gap: 4px;
cursor: pointer;
font-size: 13px;
}
/* Cleanup form group */
.form-group {
margin-bottom: 10px;
}
.form-group > label {
display: block;
font-size: 12px;
font-weight: bold;
margin-bottom: 3px;
}
h1 {
border-bottom: var(--border-width) solid var(--border-color);
padding-bottom: 10px;
@@ -1602,6 +1690,12 @@ bdi {
.follows-row:hover {
background: var(--hover-bg, rgba(255,255,255,0.05));
}
.follows-row-active {
border-left: 3px solid red;
}
.follows-row-active:hover {
border-left: 3px solid red;
}
.follows-detail td {
padding: 8px 12px;
background: var(--bg-color);
@@ -1677,3 +1771,118 @@ bdi {
.refresh-user-btn:active {
opacity: 0.7;
}
/* Upstream relay status window — monochrome per WEB.md rules */
.relay-status-summary {
font-weight: bold;
margin-bottom: 8px;
font-size: 13px;
}
.relay-status-list {
display: flex;
flex-direction: column;
gap: 2px;
max-height: 300px;
overflow-y: auto;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
padding: 4px;
}
.relay-status-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 3px 6px;
border-radius: var(--border-radius);
font-size: 12px;
font-family: var(--font-family);
border: 1px solid transparent;
}
.relay-status-url {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 8px;
}
.relay-status-badge {
flex-shrink: 0;
font-size: 10px;
padding: 1px 5px;
border: 1px solid var(--border-color);
border-radius: var(--border-radius);
white-space: nowrap;
}
/* Connected: primary color, no fill */
.relay-status-ok {
color: var(--primary-color);
}
.relay-status-ok .relay-status-badge {
border-color: var(--primary-color);
}
/* Connecting: muted, no fill */
.relay-status-connecting {
color: var(--muted-color);
}
.relay-status-connecting .relay-status-badge {
border-color: var(--muted-color);
}
/* Disconnected: muted, no fill */
.relay-status-disconnected {
color: var(--muted-color);
}
.relay-status-disconnected .relay-status-badge {
border-color: var(--muted-color);
}
/* Error: accent (red), no fill */
.relay-status-error {
color: var(--accent-color);
}
.relay-status-error .relay-status-badge {
border-color: var(--accent-color);
}
/* Unknown: muted, no fill */
.relay-status-unknown {
color: var(--muted-color);
}
.relay-status-unknown .relay-status-badge {
border-color: var(--muted-color);
}
/* Pagination bar — monochrome per WEB.md rules */
.pagination-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
font-size: 12px;
color: var(--primary-color);
}
.pagination-info {
font-style: italic;
color: var(--muted-color);
}
.pagination-buttons {
display: flex;
gap: 6px;
}
.pagination-buttons button {
font-size: 12px;
padding: 4px 12px;
cursor: pointer;
background: var(--secondary-color);
color: var(--primary-color);
border: 1px solid var(--primary-color);
border-radius: var(--border-radius);
font-family: var(--font-family);
transition: border-color 0.2s, color 0.2s;
}
.pagination-buttons button:hover {
border-color: var(--accent-color);
color: var(--accent-color);
}
.pagination-buttons button:active {
background: var(--accent-color);
color: var(--secondary-color);
border-color: var(--accent-color);
}
+11 -11
View File
@@ -1,15 +1,15 @@
New Events — Last 24 Hours
11 | X
10 | XX
9 | XX
8 | XX
7 | XX X X
6 | XX X X
5 | XX X X
4 | X XX X XX
3 | X XXX X XX
2 | X X X XXX X XX
1 | X XX X X X X X X X X X X X X XX X X XXXX X X X X X XX
21 |
19 |
17 |
15 |
13 |
11 | X
9 | X
7 | X
5 | X
3 | X X X
1 | X X X X X XX X X XX X X X X X X X X XX X X X XX X X
+--------------------------------------------------------------------------------
0s 1h 3h 4h 6h 7h 9h 10h 12h 13h 15h 16h 18h 19h 21h 22h
+1
View File
@@ -0,0 +1 @@
[{"kind":1,"count":501,"pct":56.8},{"kind":7,"count":200,"pct":22.7},{"kind":9734,"count":110,"pct":12.5},{"kind":6,"count":30,"pct":3.4},{"kind":9735,"count":20,"pct":2.3},{"kind":3,"count":11,"pct":1.2},{"kind":0,"count":10,"pct":1.1}]
+1
View File
@@ -0,0 +1 @@
[{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","name":"","count":182,"pct":20.6},{"pubkey":"eff555c8453f665bb54f907188aefba0e85c29aa9f5c30fa13d78d003c71265c","name":"Random User 4","count":70,"pct":7.9},{"pubkey":"9a90e4db8996e235a5bf1501013149d5f250b60e79336b69035f0b4dab8a4302","name":"Random User 9","count":70,"pct":7.9},{"pubkey":"b80c8c5fb175c50fe97d3a63658fa8b5dcc3e20da22dff8cef54317f6fe25394","name":"Random User 7","count":70,"pct":7.9},{"pubkey":"3d68fc5c3ddf8feef6cb6345817e4c6e421b5d292bef0be8d9a4809a23a4f0f5","name":"Random User 1","count":70,"pct":7.9},{"pubkey":"1c38f0e5446b8f5d7bc074f9f4b7afe85980706e765a5b1a3e4a52504e521ec1","name":"Random User 6","count":70,"pct":7.9},{"pubkey":"084857a219f6d73ec30fb90376e6f30161b07cd5d2130dffe47d32f229610b8a","name":"Random User 5","count":70,"pct":7.9},{"pubkey":"71691b130642de5af2b7f0a5ecf4eb374c46d913dbc72bc802005b24f9e87e11","name":"Random User 8","count":70,"pct":7.9},{"pubkey":"8d8b59e842e1529e0c448f05e570eccca82d9ca4500b758c9d44f478e96e8cdf","name":"Random User 10","count":70,"pct":7.9},{"pubkey":"f4bd9e04029c11440d545ffa66aeac45b87becf4075e2f321b0f5e8c30f187c2","name":"Random User 2","count":70,"pct":7.9},{"pubkey":"f2c0cadeee26653d9d2acd3788f6cc26d19b680d439640501eb1545afb94dd56","name":"Random User 3","count":70,"pct":7.9}]
+106
View File
@@ -56,6 +56,7 @@ $display_name = $relay_version ? ($relay_name . ' ' . $relay_version) : $relay_n
<li><button class="nav-item" data-page="relay-events">Relay Events</button></li>
<li><button class="nav-item" data-page="caching">Caching</button></li>
<li><button class="nav-item" data-page="dm">DM</button></li>
<li><button class="nav-item" data-page="cleanup">Cleanup</button></li>
<li><button class="nav-item" data-page="database">Database Query</button></li>
</ul>
<div class="nav-footer">
@@ -383,9 +384,16 @@ $display_name = $relay_version ? ($relay_name . ' ' . $relay_version) : $relay_n
<p>Loading...</p>
</div>
</div>
<div class="input-group">
<h3>Upstream Relay Status <span style="font-size:11px;font-weight:normal;color:var(--text-muted,#888)">(live connection state, updated every 15s)</span></h3>
<div id="caching-relay-status" class="status-display">
<p>Loading...</p>
</div>
</div>
<div class="input-group">
<h3>Followed Pubkeys <span style="font-size:11px;font-weight:normal;color:var(--text-muted,#888)">(click a row for per-relay details)</span></h3>
<div id="caching-follows-status" class="status-message"></div>
<div id="caching-follows-pagination" class="pagination-bar"></div>
<div class="config-table-container">
<table class="config-table" id="caching-follows-table">
<thead><tr><th>Name</th><th>npub</th><th>Root?</th><th>Events in DB</th><th>Backfill</th><th>Relays</th></tr></thead>
@@ -400,6 +408,104 @@ $display_name = $relay_version ? ($relay_name . ' ' . $relay_version) : $relay_n
</div>
</div>
<!-- EVENT CLEANUP Section -->
<div class="section" id="cleanupSection" style="display: none;">
<div class="section-header"><h2>EVENT CLEANUP</h2></div>
<!-- Saved Queries Table -->
<div class="input-group" id="saved-queries-group">
<label>Saved Queries</label>
<div class="config-table-container">
<table class="config-table" id="cleanup-saved-queries-table">
<thead><tr><th>Name</th><th>Filters</th><th>Last Preview</th><th>Last Executed</th><th>Actions</th></tr></thead>
<tbody id="cleanup-saved-queries-body">
<tr><td colspan="5" style="text-align: center; font-style: italic;">Loading...</td></tr>
</tbody>
</table>
</div>
<div class="inline-buttons" style="margin-top:8px">
<button type="button" onclick="newCleanupQuery()">New Query</button>
<button type="button" onclick="loadCleanupQueries()">Refresh</button>
</div>
</div>
<!-- Query Builder -->
<div class="input-group" id="cleanup-builder-group">
<label>Query Builder</label>
<div class="form-group">
<label for="cleanup-name">Query Name:</label>
<input type="text" id="cleanup-name" placeholder="My Cleanup Query" style="width:100%;max-width:400px">
</div>
<div class="form-group">
<label>Follows Filter:</label>
<div class="radio-group">
<label><input type="radio" name="cleanup-follows" value="all" checked> All</label>
<label><input type="radio" name="cleanup-follows" value="follows"> Follows only</label>
<label><input type="radio" name="cleanup-follows" value="non_follows"> Non-follows only</label>
</div>
</div>
<div class="form-group">
<label for="cleanup-kinds">Event Kinds (comma-separated, empty = all):</label>
<input type="text" id="cleanup-kinds" placeholder="e.g. 1, 7, 9734" style="width:100%;max-width:400px">
<div style="font-size:11px;color:var(--text-muted,#888);margin-top:2px">
Common kinds: 0 (profiles), 1 (text), 3 (contacts), 4 (DM), 5 (delete),
6 (repost), 7 (reaction), 9734 (zap request), 9735 (zap receipt), 10002 (relay list)
</div>
</div>
<div class="form-group">
<label>From (delete events after this):</label>
<div class="datetime-group">
<input type="date" id="cleanup-from-date" class="cleanup-date-input">
<input type="time" id="cleanup-from-time" class="cleanup-time-input">
<span class="datetime-hint">leave empty for no bound</span>
</div>
</div>
<div class="form-group">
<label>To (delete events before this):</label>
<div class="datetime-group">
<input type="date" id="cleanup-to-date" class="cleanup-date-input">
<input type="time" id="cleanup-to-time" class="cleanup-time-input">
<span class="datetime-hint">leave empty for no bound</span>
</div>
</div>
<div class="form-group">
<label for="cleanup-max-events">Max Events (0 = no limit):</label>
<input type="number" id="cleanup-max-events" value="0" min="0" style="width:120px">
</div>
<div class="inline-buttons" style="margin-top:8px">
<button type="button" onclick="previewCleanup()">Preview</button>
<button type="button" onclick="saveCleanupQuery()">Save Query</button>
<button type="button" onclick="executeCleanup()" style="background:#c0392b;color:#fff">Execute Delete</button>
</div>
</div>
<!-- Results Area -->
<div class="input-group" id="cleanup-results-group" style="display:none;">
<label>Results</label>
<div id="cleanup-results-summary" class="info-box"></div>
<div id="cleanup-results-breakdown" class="config-table-container" style="margin-top:8px"></div>
<div id="cleanup-results-sql" style="margin-top:8px">
<details>
<summary style="cursor:pointer;font-size:12px;color:var(--text-muted,#888)">SQL Preview</summary>
<pre id="cleanup-sql-preview" style="background:var(--bg-code,#f5f5f5);padding:8px;border-radius:4px;overflow-x:auto;font-size:11px;margin-top:4px;white-space:pre-wrap"></pre>
</details>
</div>
</div>
<!-- Confirmation Dialog -->
<div id="cleanup-confirm-dialog" class="modal-overlay" style="display:none;">
<div class="modal-content" style="background:var(--bg-card,#fff);border:1px solid var(--border,#ccc);border-radius:8px;padding:20px;max-width:480px;margin:10% auto;box-shadow:0 4px 20px rgba(0,0,0,0.3)">
<h3 style="margin-top:0">Confirm Delete</h3>
<p id="cleanup-confirm-msg">Are you sure you want to delete these events?</p>
<p style="color:#c0392b;font-weight:bold">This action cannot be undone.</p>
<div class="inline-buttons" style="margin-top:12px">
<button type="button" id="cleanup-confirm-btn" onclick="executeCleanupConfirmed()" style="background:#c0392b;color:#fff">Delete</button>
<button type="button" onclick="document.getElementById('cleanup-confirm-dialog').style.display='none'">Cancel</button>
</div>
</div>
</div>
</div>
<!-- SQL QUERY Section -->
<div class="section" id="sqlQuerySection" style="display: none;">
<div class="section-header"><h2>SQL QUERY CONSOLE</h2></div>
+4010 -8754
View File
File diff suppressed because it is too large Load Diff
+180
View File
@@ -0,0 +1,180 @@
<?php
/**
* admin/public/index.php — Public relay stats page.
*
* Served at https://<domain>/relay/stats/ (no authentication required).
*
* Shows the relay name/description/pubkey (read-only, from the config table)
* and the server-rendered ASCII event-rate chart. The chart is fetched from
* the public endpoint at /relay/api/chart.php?range=...
*
* This page exposes ONLY read-only, non-sensitive information:
* - relay_name, relay_description, relay_pubkey, relay_version (from config)
* - the event-rate chart (aggregate COUNT query, no user data)
*
* All sensitive admin endpoints (config edits, auth rules, IP bans, DMs,
* raw SQL queries) remain behind Basic Auth at /relay/admin/.
*/
require_once __DIR__ . '/../lib/helpers.php';
$pdo = db();
// Fetch relay info for the header (from config table).
$relay_name = '';
$relay_desc = '';
$relay_pubkey = '';
$relay_version = '';
try {
$relay_name = $pdo->query("SELECT value FROM config WHERE key = 'relay_name'")->fetchColumn() ?: 'C-Relay-PG';
$relay_desc = $pdo->query("SELECT value FROM config WHERE key = 'relay_description'")->fetchColumn() ?: '';
$relay_pubkey = $pdo->query("SELECT value FROM config WHERE key = 'relay_pubkey'")->fetchColumn() ?: '';
$relay_version = $pdo->query("SELECT value FROM config WHERE key = 'relay_version'")->fetchColumn() ?: '';
} catch (PDOException $e) {}
// Convert relay pubkey (hex) to npub and format into 3 lines of 3 groups of 7 chars.
$relay_npub = $relay_pubkey;
if ($relay_pubkey && preg_match('/^[0-9a-fA-F]{64}$/', $relay_pubkey)) {
$relay_npub = hex_to_npub($relay_pubkey);
}
$formatted_npub = $relay_npub;
if (strlen($relay_npub) === 63) {
$line1 = substr($relay_npub, 0, 7) . ' ' . substr($relay_npub, 7, 7) . ' ' . substr($relay_npub, 14, 7);
$line2 = substr($relay_npub, 21, 7) . ' ' . substr($relay_npub, 28, 7) . ' ' . substr($relay_npub, 35, 7);
$line3 = substr($relay_npub, 42, 7) . ' ' . substr($relay_npub, 49, 7) . ' ' . substr($relay_npub, 56, 7);
$formatted_npub = $line1 . "\n" . $line2 . "\n" . $line3;
}
$display_name = $relay_version ? ($relay_name . ' ' . $relay_version) : $relay_name;
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= e($display_name) ?></title>
<link rel="stylesheet" href="/relay/assets/index.css">
<style>
/* Public page uses the admin theme but hides the nav and centers content. */
.side-nav, .side-nav-overlay { display: none !important; }
.public-wrap { max-width: 1100px; margin: 0 auto; padding: 20px; }
.public-chart-box {
background: var(--secondary-color);
border: var(--border-width) solid var(--border-color);
border-radius: var(--border-radius);
padding: 12px;
margin-top: 16px;
}
.public-footer {
margin-top: 24px;
padding-top: 12px;
border-top: var(--border-width) solid var(--border-color);
font-size: 12px;
color: var(--muted-color);
text-align: center;
}
.public-footer a { color: var(--accent-color); text-decoration: none; }
.event-rate-chart-container {
white-space: pre;
font-family: var(--font-family);
font-size: 12px;
line-height: 1.2;
overflow-x: auto;
}
</style>
</head>
<body>
<!-- Header with title and relay info (read-only) -->
<div class="section">
<div class="header-content">
<div class="header-title">
<span class="relay-letter" data-letter="R">R</span>
<span class="relay-letter" data-letter="E">E</span>
<span class="relay-letter" data-letter="L">L</span>
<span class="relay-letter" data-letter="A">A</span>
<span class="relay-letter" data-letter="Y">Y</span>
</div>
<div class="relay-info">
<div id="relay-name" class="relay-name"><?= e($display_name) ?></div>
<div id="relay-description" class="relay-description"><?= e($relay_desc) ?></div>
<div id="relay-pubkey-container" class="relay-pubkey-container" title="Click to copy npub">
<div id="relay-pubkey" class="relay-pubkey"><?= e($formatted_npub) ?></div>
</div>
</div>
</div>
</div>
<div class="public-wrap">
<div class="section-header">EVENT RATE</div>
<!-- Range selector tabs -->
<div class="chart-range-tabs">
<button class="chart-tab active" data-range="hour">1H</button>
<button class="chart-tab" data-range="day">1D</button>
<button class="chart-tab" data-range="month">1M</button>
<button class="chart-tab" data-range="year">1Y</button>
</div>
<!-- Server-rendered ASCII chart (fetched from the public chart endpoint) -->
<div class="public-chart-box">
<div id="event-rate-chart" class="event-rate-chart-container">Loading chart...</div>
</div>
<div class="public-footer">
Public relay statistics &middot;
<a href="wss://<?= e($_SERVER['HTTP_HOST'] ?? '') ?>">wss://<?= e($_SERVER['HTTP_HOST'] ?? '') ?></a>
&middot; Admin? visit <a href="/relay/admin/">/relay/admin/</a>
</div>
</div>
<script>
// Minimal chart loader — fetches the public ASCII chart endpoint and
// injects the plain-text response into the chart container. No auth.
const chartEl = document.getElementById('event-rate-chart');
let currentRange = 'hour';
let chartTimer = null;
async function loadChart(range) {
currentRange = range;
if (!chartEl) return;
chartEl.textContent = 'Loading chart...';
try {
const res = await fetch('/relay/api/chart.php?range=' + encodeURIComponent(range));
if (!res.ok) { chartEl.textContent = 'Chart load failed (HTTP ' + res.status + ')'; return; }
const text = await res.text();
chartEl.textContent = text || '(no data)';
} catch (e) {
chartEl.textContent = 'Chart load failed: ' + e.message;
}
}
document.querySelectorAll('.chart-tab').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.chart-tab').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const range = btn.getAttribute('data-range');
loadChart(range);
// Hour chart is live — auto-refresh every 30s. Others are cached server-side.
if (chartTimer) { clearInterval(chartTimer); chartTimer = null; }
if (range === 'hour') {
chartTimer = setInterval(() => loadChart(currentRange), 30000);
}
});
});
// Initial load + live refresh for the default (hour) range.
loadChart('hour');
chartTimer = setInterval(() => loadChart(currentRange), 30000);
// Click-to-copy the relay npub.
const pkContainer = document.getElementById('relay-pubkey-container');
if (pkContainer) {
pkContainer.style.cursor = 'pointer';
pkContainer.addEventListener('click', () => {
const txt = (document.getElementById('relay-pubkey').textContent || '').replace(/\s+/g, '');
if (txt && navigator.clipboard) navigator.clipboard.writeText(txt);
});
}
</script>
</body>
</html>
+16 -78
View File
@@ -2,6 +2,7 @@
# Build fully static MUSL binaries for C-Relay-PG using Alpine Docker
# Produces truly portable binaries with zero runtime dependencies
# PostgreSQL backend only.
set -e
@@ -11,7 +12,6 @@ DOCKERFILE="$SCRIPT_DIR/Dockerfile.alpine-musl"
# Parse command line arguments
DEBUG_BUILD=false
DB_BACKEND="${DB_BACKEND:-postgres}"
while [[ $# -gt 0 ]]; do
case "$1" in
@@ -19,31 +19,14 @@ while [[ $# -gt 0 ]]; do
DEBUG_BUILD=true
shift
;;
--db-backend)
if [[ -z "$2" ]]; then
echo "ERROR: --db-backend requires a value (sqlite|postgres)"
exit 1
fi
DB_BACKEND="$2"
shift 2
;;
--db-backend=*)
DB_BACKEND="${1#*=}"
shift
;;
*)
echo "ERROR: Unknown argument: $1"
echo "Usage: $0 [--debug] [--db-backend postgres|sqlite]"
echo "Usage: $0 [--debug]"
exit 1
;;
esac
done
if [[ "$DB_BACKEND" != "sqlite" && "$DB_BACKEND" != "postgres" ]]; then
echo "ERROR: Invalid DB backend '$DB_BACKEND'. Use sqlite or postgres."
exit 1
fi
if [[ "$DEBUG_BUILD" == "true" ]]; then
echo "=========================================="
echo "C-Relay-PG MUSL Static Binary Builder (DEBUG MODE)"
@@ -56,7 +39,7 @@ fi
echo "Project directory: $SCRIPT_DIR"
echo "Build directory: $BUILD_DIR"
echo "Debug build: $DEBUG_BUILD"
echo "DB backend: $DB_BACKEND"
echo "DB backend: postgres"
echo ""
# Create build directory
@@ -155,7 +138,6 @@ echo ""
$DOCKER_CMD build \
--platform "$PLATFORM" \
--build-arg DEBUG_BUILD=$DEBUG_BUILD \
--build-arg DB_BACKEND=$DB_BACKEND \
-f "$DOCKERFILE" \
-t c-relay-pg-musl-builder:latest \
--progress=plain \
@@ -179,7 +161,6 @@ echo "=========================================="
$DOCKER_CMD build \
--platform "$PLATFORM" \
--build-arg DEBUG_BUILD=$DEBUG_BUILD \
--build-arg DB_BACKEND=$DB_BACKEND \
--target builder \
-f "$DOCKERFILE" \
-t c-relay-pg-static-builder-stage:latest \
@@ -198,74 +179,31 @@ $DOCKER_CMD cp "$CONTAINER_ID:/build/c_relay_pg_static" "$BUILD_DIR/$OUTPUT_NAME
# Clean up container
$DOCKER_CMD rm "$CONTAINER_ID" > /dev/null
echo "✓ Binary extracted to: $BUILD_DIR/$OUTPUT_NAME"
echo "✓ Binary extracted: $BUILD_DIR/$OUTPUT_NAME"
echo ""
# Make binary executable
chmod +x "$BUILD_DIR/$OUTPUT_NAME"
# Verify the binary
echo "=========================================="
echo "Step 3: Verifying static binary"
echo "=========================================="
echo ""
file "$BUILD_DIR/$OUTPUT_NAME"
ls -lh "$BUILD_DIR/$OUTPUT_NAME"
echo "Checking for dynamic dependencies:"
if LDD_OUTPUT=$(timeout 5 ldd "$BUILD_DIR/$OUTPUT_NAME" 2>&1); then
if echo "$LDD_OUTPUT" | grep -q "not a dynamic executable"; then
echo "✓ Binary is fully static (no dynamic dependencies)"
TRULY_STATIC=true
elif echo "$LDD_OUTPUT" | grep -q "statically linked"; then
echo "✓ Binary is statically linked"
TRULY_STATIC=true
else
echo "⚠ WARNING: Binary may have dynamic dependencies:"
echo "$LDD_OUTPUT"
TRULY_STATIC=false
fi
# Check for dynamic dependencies
if ldd "$BUILD_DIR/$OUTPUT_NAME" 2>&1 | grep -q "not a dynamic executable"; then
echo "✓ Binary is truly static (no dynamic dependencies)"
else
# ldd failed or timed out - check with file command instead
if file "$BUILD_DIR/$OUTPUT_NAME" | grep -q "statically linked"; then
echo "✓ Binary is statically linked (verified with file command)"
TRULY_STATIC=true
else
echo "⚠ Could not verify static linking (ldd check failed)"
TRULY_STATIC=false
fi
echo "⚠ Binary has dynamic dependencies:"
ldd "$BUILD_DIR/$OUTPUT_NAME" 2>&1
fi
echo ""
echo "File size: $(ls -lh "$BUILD_DIR/$OUTPUT_NAME" | awk '{print $5}')"
echo ""
# Test if binary runs
echo "Testing binary execution:"
if "$BUILD_DIR/$OUTPUT_NAME" --version 2>&1 | head -5; then
echo "✓ Binary executes successfully"
else
echo "⚠ Binary execution test failed (this may be normal if --version is not supported)"
fi
echo ""
# Summary
echo "=========================================="
echo "Build Summary"
echo "Build complete!"
echo "=========================================="
echo "Binary: $BUILD_DIR/$OUTPUT_NAME"
echo "Size: $(du -h "$BUILD_DIR/$OUTPUT_NAME" | cut -f1)"
echo "Platform: $PLATFORM"
if [ "$DEBUG_BUILD" = true ]; then
echo "Build Type: DEBUG (with symbols, no optimization)"
else
echo "Build Type: PRODUCTION (optimized, stripped)"
fi
echo "DB Backend: $DB_BACKEND"
if [ "$TRULY_STATIC" = true ]; then
echo "Linkage: Fully static binary (Alpine MUSL-based)"
echo "Portability: Works on ANY Linux distribution"
else
echo "Linkage: Static binary (may have minimal dependencies)"
fi
echo ""
echo "✓ Build complete!"
echo "Size: $(ls -lh "$BUILD_DIR/$OUTPUT_NAME" | awk '{print $5}')"
echo ""
# Clean up Docker builder stage image
$DOCKER_CMD rmi c-relay-pg-static-builder-stage:latest > /dev/null 2>&1 || true
+4 -1
View File
@@ -25,6 +25,7 @@ RUN apk add --no-cache \
curl-dev \
curl-static \
sqlite-dev \
postgresql-dev \
linux-headers \
wget \
bash
@@ -103,13 +104,15 @@ RUN if [ "$DEBUG_BUILD" = "true" ]; then \
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
-I. -Isrc -Inostr_core_lib -Inostr_core_lib/nostr_core \
-Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \
-I/usr/include/postgresql \
src/main.c src/debug.c src/jsonc_strip.c src/config.c src/state.c \
src/follow_graph.c src/relay_sink.c src/live_subscriber.c \
src/backfill.c src/relay_discovery.c \
src/pg_inbox.c src/pg_config.c src/forward_catchup.c \
-o /build/caching_relay_static \
nostr_core_lib/libnostr_core.a \
-lwebsockets -lssl -lcrypto -lsecp256k1 \
-lcurl -lz -lpthread -lm -ldl && \
-lcurl -lz -lpthread -lm -ldl -lpq -lpgcommon -lpgport && \
eval "$STRIP_CMD"
# Verify it's truly static
+21 -3
View File
@@ -61,7 +61,25 @@ $(TARGET): $(MAIN_SRC) $(NOSTR_CORE_LIB)
rm -rf /tmp/cr_lib && \
echo "Build complete: $(TARGET)"
clean:
rm -f $(TARGET)
# ------------------------------------------------------------------ #
# cache_all_test - feasibility test for "cache everything" approach #
# ------------------------------------------------------------------ #
TEST_TARGET = ../build/cache_all_test
TEST_SRC = src/cache_all_test.c src/debug.c src/jsonc_strip.c src/config.c \
src/state.c
.PHONY: all clean
$(TEST_TARGET): $(TEST_SRC) $(NOSTR_CORE_LIB)
@echo "Compiling cache_all_test for architecture: $(ARCH)"
@rm -rf /tmp/cr_lib && mkdir -p /tmp/cr_lib && \
ar x $(NOSTR_CORE_LIB) --output=/tmp/cr_lib && \
echo "Extracted $$(ls /tmp/cr_lib/*.o | wc -l) objects" && \
$(CC) $(CFLAGS) $(INCLUDES) $(TEST_SRC) /tmp/cr_lib/*.o -o $(TEST_TARGET) $(LIBS) && \
rm -rf /tmp/cr_lib && \
echo "Build complete: $(TEST_TARGET)"
cache_all_test: $(TEST_TARGET)
clean:
rm -f $(TARGET) $(TEST_TARGET)
.PHONY: all clean cache_all_test
+56
View File
@@ -0,0 +1,56 @@
{
// Cache-All Feasibility Test Config
// Root npubs to resolve follow graph from
"root_npubs": [
"npub1rmz9gu6de0m0u4ysrn39crrud099ahvfgs6pvasl4hpjr5ud7yus54xv06"
],
// Relays to test (from server relay list, connected ones only)
"upstream_relays": [
"wss://laantungir.net",
"wss://nos.lol",
"wss://nostr-pub.wellorder.net",
"wss://nostr.mom",
"wss://nostr.oxtr.dev",
"wss://offchain.pub",
"wss://premium.primal.net",
"wss://relay.damus.io",
"wss://relay.ditto.pub",
"wss://relay.divine.video",
"wss://relay.momostr.pink",
"wss://relay.mostr.pub",
"wss://relay.nostrplebs.com",
"wss://relay.primal.net",
"wss://theforest.nostr1.com"
],
// Local relay (not used by test, but required by config parser)
"local_relay": "ws://localhost:8888",
// Kinds to follow (not used by test, but required by config parser)
"kinds": [0, 1, 3, 5, 6, 7, 9735, 10002, 30023],
// Admin kinds
"admin_kinds": ["*"],
// Backfill config (not used by test)
"backfill": {
"enabled": false,
"events_per_tick": 500,
"tick_interval_seconds": 5
},
// Live config (not used by test)
"live": {
"enabled": false,
"resubscribe_interval_seconds": 300
},
// Follow graph refresh (not used by test)
"follow_graph_refresh_seconds": 600,
// State (not used by test)
"state": {
"backfilled_until": 0
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"root_npubs": [
"npub1rmz9gu6de0m0u4ysrn39crrud099ahvfgs6pvasl4hpjr5ud7yus54xv06"
],
"upstream_relays": [
"wss://relay.primal.net"
],
"local_relay": "ws://localhost:8888",
"kinds": [0, 1, 3, 5, 6, 7, 9735, 10002, 30023],
"admin_kinds": ["*"],
"backfill": { "enabled": false, "events_per_tick": 500, "tick_interval_seconds": 5 },
"live": { "enabled": false, "resubscribe_interval_seconds": 300 },
"follow_graph_refresh_seconds": 600,
"state": { "backfilled_until": 0 }
}
@@ -0,0 +1,253 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 5m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 11200
Total bytes: 17099684 (16.31 MB)
Avg event size: 1527 bytes
Max event: 45848 bytes (kind 3)
Unique pubkeys: 4292
Relays contacted: 3
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 3492 1 0 0 0 0 DOWN
wss://nos.lol 5613 0 0 0 0 0 DOWN
wss://relay.damus.io 2095 0 0 0 0 0 DOWN
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 3060 27.3% 8988132 2937 19622
5 Deletion 1124 10.0% 498378 443 664
24747 Ephemeral 1020 9.1% 602037 590 671
22668 Ephemeral 547 4.9% 234663 429 429
30078 App data 439 3.9% 1714659 3906 37853
20001 Ephemeral (key xchg) 395 3.5% 266277 674 1719
22734 Ephemeral 265 2.4% 113155 427 427
4515 Unknown 246 2.2% 390676 1588 8760
1 Text note 205 1.8% 164127 801 2024
1059 Gift wrap seal 188 1.7% 301940 1606 5285
23334 Ephemeral 179 1.6% 77621 434 438
38501 App data (btc) 157 1.4% 116283 741 744
30382 Addressable 138 1.2% 66879 485 498
21050 Ephemeral 132 1.2% 128700 975 2558
10002 Relay list 127 1.1% 178988 1409 42385
22850 Ephemeral 124 1.1% 53320 430 430
31991 Addressable 122 1.1% 56242 461 461
0 Profile metadata 120 1.1% 92899 774 7180
37195 Addressable 102 0.9% 77276 758 832
24242 Ephemeral 93 0.8% 56549 608 3281
25050 Ephemeral 90 0.8% 39930 444 465
7 Reaction 79 0.7% 42454 537 738
23337 Ephemeral 73 0.7% 37477 513 519
24133 Ephemeral 71 0.6% 146874 2069 7334
445 Encrypted group msg 68 0.6% 386304 5681 38736
22601 Ephemeral 62 0.6% 26536 428 428
22613 Ephemeral 58 0.5% 40166 693 695
22749 Ephemeral 56 0.5% 24024 429 429
22699 Ephemeral 56 0.5% 23968 428 428
22676 Ephemeral 56 0.5% 23912 427 427
23003 Ephemeral 50 0.4% 21400 428 428
13194 NWC info 50 0.4% 25490 510 514
29333 Ephemeral 50 0.4% 24600 492 492
22795 Ephemeral 50 0.4% 22860 457 1839
22774 Ephemeral 50 0.4% 21401 428 429
20000 Ephemeral 50 0.4% 23047 461 536
22655 Ephemeral 49 0.4% 20972 428 428
31990 App handler info 46 0.4% 43151 938 1465
5300 Unknown 44 0.4% 27099 616 701
30383 Addressable 44 0.4% 23495 534 542
22456 Ephemeral 43 0.4% 21378 497 790
25555 Ephemeral 42 0.4% 175994 4190 16987
20004 Ephemeral 39 0.3% 20619 529 549
3927 Unknown 39 0.3% 20809 534 544
22484 Ephemeral 36 0.3% 32790 911 1165
11998 Unknown 33 0.3% 15297 464 465
7368 Unknown 32 0.3% 33232 1038 1041
20387 Ephemeral 31 0.3% 15934 514 514
22507 Ephemeral 28 0.2% 25861 924 1163
20787 Ephemeral 26 0.2% 34212 1316 2282
10100 Unknown 25 0.2% 48552 1942 3825
30091 Addressable 23 0.2% 14133 614 629
30088 Addressable 23 0.2% 42847 1863 1871
38502 Addressable 22 0.2% 8470 385 385
4 Encrypted DM 22 0.2% 25752 1171 6250
1985 Label 22 0.2% 10436 474 483
10050 DM relays 21 0.2% 9383 447 465
30315 User status 20 0.2% 12697 635 926
22816 Ephemeral 20 0.2% 8580 429 429
4004 Unknown 19 0.2% 22211 1169 1169
22522 Ephemeral 18 0.2% 7632 424 424
22914 Ephemeral 17 0.2% 15202 894 1819
3 Follow list 15 0.1% 210884 14059 45848
22591 Ephemeral 15 0.1% 6390 426 426
30089 Chunked data 15 0.1% 221528 14769 44372
6300 Unknown 15 0.1% 83624 5575 17374
30023 Long-form article 15 0.1% 83310 5554 8177
20333 Ephemeral 15 0.1% 11950 797 798
22832 Ephemeral 14 0.1% 6006 429 429
1077 Unknown 14 0.1% 8498 607 607
22651 Ephemeral 14 0.1% 5992 428 428
30515 Addressable 14 0.1% 55160 3940 3940
23333 Ephemeral 11 0.1% 6455 587 601
2333 Unknown 11 0.1% 7423 675 689
30311 Addressable 10 0.1% 10569 1057 1214
21617 Ephemeral 10 0.1% 4950 495 495
30443 Addressable 10 0.1% 11954 1195 1292
9735 Zap receipt 10 0.1% 20601 2060 2282
1337 Unknown 10 0.1% 5696 570 811
25055 Ephemeral 10 0.1% 14724 1472 1764
21060 Ephemeral 9 0.1% 5130 570 570
38225 Addressable 9 0.1% 9197 1022 1047
22949 Ephemeral 9 0.1% 3879 431 431
4244 Unknown 9 0.1% 5076 564 564
7375 Unknown 9 0.1% 72189 8021 14089
7376 Unknown 9 0.1% 4689 521 521
38385 Addressable 9 0.1% 12280 1364 1496
31234 Addressable 9 0.1% 12744 1416 1416
22712 Ephemeral 8 0.1% 3424 428 428
38503 Addressable 8 0.1% 4424 553 553
30113 Addressable 8 0.1% 4688 586 701
6 Repost 8 0.1% 11116 1390 2553
31510 Addressable 7 0.1% 6853 979 982
22785 Ephemeral 7 0.1% 2996 428 428
22680 Ephemeral 7 0.1% 6191 884 1160
22793 Ephemeral 7 0.1% 2996 428 428
22684 Ephemeral 6 0.1% 2574 429 429
22225 Ephemeral 6 0.1% 2661 444 444
1002 Unknown 6 0.1% 5458 910 971
22739 Ephemeral 6 0.1% 2568 428 428
22658 Ephemeral 6 0.1% 5377 896 1164
30080 Addressable 5 0.0% 4658 932 985
33334 Addressable 5 0.0% 4155 831 831
10011 Unknown 5 0.0% 2155 431 431
25195 Ephemeral 5 0.0% 3390 678 678
22508 Ephemeral 5 0.0% 2130 426 426
22789 Ephemeral 5 0.0% 2140 428 428
30380 Addressable 5 0.0% 2270 454 454
38384 Addressable 5 0.0% 3715 743 743
20384 Ephemeral 5 0.0% 3195 639 639
22688 Ephemeral 5 0.0% 2150 430 430
22649 Ephemeral 5 0.0% 2135 427 427
39101 Addressable 5 0.0% 2665 533 533
22611 Ephemeral 5 0.0% 2130 426 426
22526 Ephemeral 5 0.0% 2150 430 430
22514 Ephemeral 4 0.0% 1700 425 425
22775 Ephemeral 4 0.0% 1716 429 429
443 Unknown 4 0.0% 4336 1084 1084
25196 Ephemeral 4 0.0% 7152 1788 2214
30618 Addressable 4 0.0% 6570 1642 4371
23194 NWC request 4 0.0% 2106 526 576
1040 Unknown 4 0.0% 4192 1048 1048
38500 Addressable 4 0.0% 2848 712 713
22236 Ephemeral 4 0.0% 3109 777 801
1111 Comment 4 0.0% 5593 1398 1988
3434 Unknown 3 0.0% 2035 678 705
1050 Unknown 3 0.0% 1311 437 437
33051 Addressable 3 0.0% 1559 520 522
7000 Unknown 3 0.0% 2329 776 779
22309 Ephemeral 3 0.0% 1331 444 444
30617 Addressable 3 0.0% 3267 1089 1089
10030 Emoji list 3 0.0% 1215 405 405
22253 Ephemeral 3 0.0% 1331 444 444
22345 Ephemeral 3 0.0% 1331 444 444
4245 Unknown 3 0.0% 1332 444 444
22423 Ephemeral 3 0.0% 1331 444 444
4243 Unknown 3 0.0% 1692 564 564
10000 Mute list 3 0.0% 4678 1559 3850
22352 Ephemeral 3 0.0% 1330 443 444
38383 Addressable 3 0.0% 2159 720 743
22327 Ephemeral 3 0.0% 1330 443 444
30213 Addressable 3 0.0% 1880 627 637
20 Picture 2 0.0% 1272 636 827
10312 Unknown 2 0.0% 973 486 491
10007 Search relays 2 0.0% 1272 636 754
10086 Unknown 2 0.0% 1451 726 757
22891 Ephemeral 2 0.0% 2272 1136 1843
22495 Ephemeral 2 0.0% 854 427 427
4242 Unknown 2 0.0% 12868 6434 6443
30000 Follow sets 2 0.0% 1773 886 950
22777 Ephemeral 2 0.0% 856 428 428
22391 Ephemeral 2 0.0% 887 444 444
6472 Unknown 2 0.0% 1106 553 553
22239 Ephemeral 2 0.0% 887 444 444
22146 Ephemeral 2 0.0% 886 443 443
22420 Ephemeral 2 0.0% 887 444 444
10063 Unknown 2 0.0% 1012 506 627
22193 Ephemeral 2 0.0% 886 443 443
22275 Ephemeral 2 0.0% 886 443 443
31000 Addressable 2 0.0% 1204 602 616
22738 Ephemeral 2 0.0% 858 429 429
31340 Addressable 2 0.0% 854 427 430
22218 Ephemeral 2 0.0% 887 444 444
22285 Ephemeral 2 0.0% 887 444 444
7403 Unknown 2 0.0% 982 491 491
22302 Ephemeral 2 0.0% 886 443 443
22241 Ephemeral 2 0.0% 887 444 444
22288 Ephemeral 2 0.0% 887 444 444
22120 Ephemeral 2 0.0% 2170 1085 1118
22119 Ephemeral 2 0.0% 1691 846 846
15750 Unknown 2 0.0% 1184 592 620
10443 Unknown 2 0.0% 2578 1289 1289
22736 Ephemeral 1 0.0% 429 429 429
22653 Ephemeral 1 0.0% 1840 1840 1840
22842 Ephemeral 1 0.0% 1847 1847 1847
22190 Ephemeral 1 0.0% 443 443 443
37779 Addressable 1 0.0% 523 523 523
22312 Ephemeral 1 0.0% 443 443 443
1717 Unknown 1 0.0% 1776 1776 1776
22407 Ephemeral 1 0.0% 425 425 425
1340 Unknown 1 0.0% 435 435 435
10051 Unknown 1 0.0% 443 443 443
10012 Unknown 1 0.0% 522 522 522
22368 Ephemeral 1 0.0% 443 443 443
10003 Bookmarks 1 0.0% 1974 1974 1974
39015 Addressable 1 0.0% 696 696 696
1311 Unknown 1 0.0% 534 534 534
22820 Ephemeral 1 0.0% 430 430 430
22928 Ephemeral 1 0.0% 429 429 429
22713 Ephemeral 1 0.0% 429 429 429
22543 Ephemeral 1 0.0% 1843 1843 1843
22369 Ephemeral 1 0.0% 443 443 443
22314 Ephemeral 1 0.0% 443 443 443
10001 Pinned notes 1 0.0% 710 710 710
30815 Addressable 1 0.0% 37534 37534 37534
31339 Addressable 1 0.0% 1470 1470 1470
38422 Addressable 1 0.0% 20019 20019 20019
30087 Addressable 1 0.0% 7325 7325 7325
30090 Addressable 1 0.0% 1515 1515 1515
10089 Unknown 1 0.0% 1097 1097 1097
13196 Unknown 1 0.0% 656 656 656
39050 Addressable 1 0.0% 1843 1843 1843
10005 Unknown 1 0.0% 694 694 694
30554 Addressable 1 0.0% 609 609 609
22765 Ephemeral 1 0.0% 431 431 431
1078 Unknown 1 0.0% 13949 13949 13949
22740 Ephemeral 1 0.0% 3194 3194 3194
38111 Addressable 1 0.0% 747 747 747
4747 Unknown 1 0.0% 721 721 721
10019 Unknown 1 0.0% 536 536 536
22333 Ephemeral 1 0.0% 443 443 443
22318 Ephemeral 1 0.0% 443 443 443
23010 Ephemeral 1 0.0% 429 429 429
30079 Addressable 1 0.0% 875 875 875
22455 Ephemeral 1 0.0% 1840 1840 1840
22219 Ephemeral 1 0.0% 443 443 443
30787 Addressable 1 0.0% 1370 1370 1370
22624 Ephemeral 1 0.0% 3198 3198 3198
22750 Ephemeral 1 0.0% 553 553 553
22313 Ephemeral 1 0.0% 443 443 443
1080 Unknown 1 0.0% 1033 1033 1033
11316 Unknown 1 0.0% 725 725 725
11317 Unknown 1 0.0% 12051 12051 12051
12222 Unknown 1 0.0% 579 579 579
22823 Ephemeral 1 0.0% 429 429 429
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 09:32:17 2026
# Config: cache_all_test_config.jsonc
# Duration: 300 seconds
[2026-08-02 09:32:24] [RELAY] wss://relay.primal.net EOSE
@@ -0,0 +1,207 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 2m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 4267
Total bytes: 6726611 (6.41 MB)
Avg event size: 1576 bytes
Max event: 42385 bytes (kind 10002)
Unique pubkeys: 1860
Relays contacted: 3
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 1670 1 0 0 0 0 OK
wss://nos.lol 1792 0 0 0 0 0 OK
wss://relay.damus.io 805 0 0 0 0 0 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 1199 28.1% 3547818 2959 5314
5 Deletion 466 10.9% 204587 439 1064
30078 App data 222 5.2% 948782 4274 40337
22668 Ephemeral 221 5.2% 94809 429 429
20001 Ephemeral (key xchg) 152 3.6% 106536 701 1755
4515 Unknown 152 3.6% 260732 1715 8759
22734 Ephemeral 108 2.5% 46116 427 427
1 Text note 105 2.5% 83026 791 3482
21050 Ephemeral 91 2.1% 89206 980 2558
24747 Ephemeral 85 2.0% 42775 503 671
22850 Ephemeral 77 1.8% 33110 430 430
22591 Ephemeral 66 1.5% 53607 812 1222
38501 App data (btc) 61 1.4% 45175 741 744
10002 Relay list 51 1.2% 63749 1250 42385
0 Profile metadata 47 1.1% 33672 716 1611
37195 Addressable 44 1.0% 32855 747 771
24242 Ephemeral 37 0.9% 21423 579 579
25050 Ephemeral 36 0.8% 15972 444 465
7 Reaction 36 0.8% 18965 527 646
22613 Ephemeral 35 0.8% 26154 747 1134
22484 Ephemeral 32 0.7% 29125 910 1163
22749 Ephemeral 29 0.7% 12441 429 429
24133 Ephemeral 25 0.6% 37446 1498 7334
22601 Ephemeral 25 0.6% 10700 428 428
22507 Ephemeral 24 0.6% 22582 941 1165
1059 Gift wrap seal 23 0.5% 41391 1800 2897
22699 Ephemeral 23 0.5% 9844 428 428
22676 Ephemeral 23 0.5% 9821 427 427
22774 Ephemeral 20 0.5% 8560 428 428
22795 Ephemeral 20 0.5% 8580 429 429
22655 Ephemeral 20 0.5% 8560 428 428
23003 Ephemeral 20 0.5% 8560 428 428
30382 Addressable 20 0.5% 9960 498 498
29333 Ephemeral 20 0.5% 9840 492 492
25555 Ephemeral 18 0.4% 62434 3469 17003
11998 Unknown 18 0.4% 8322 462 465
31990 App handler info 18 0.4% 16845 936 1465
4 Encrypted DM 18 0.4% 28064 1559 17018
13194 NWC info 18 0.4% 9252 514 514
22689 Ephemeral 17 0.4% 7259 427 427
5300 Unknown 17 0.4% 10495 617 701
445 Encrypted group msg 17 0.4% 81892 4817 19620
20000 Ephemeral 15 0.4% 7045 470 546
20387 Ephemeral 14 0.3% 7196 514 514
20004 Ephemeral 14 0.3% 7488 535 549
7368 Unknown 13 0.3% 13476 1037 1040
22456 Ephemeral 13 0.3% 6110 470 470
9735 Zap receipt 13 0.3% 26072 2006 2331
22551 Ephemeral 13 0.3% 11042 849 1161
22760 Ephemeral 13 0.3% 11014 847 1161
22658 Ephemeral 13 0.3% 11050 850 1164
10100 Unknown 11 0.3% 20684 1880 3825
30315 User status 11 0.3% 8899 809 1038
20787 Ephemeral 11 0.3% 15458 1405 2474
22236 Ephemeral 10 0.2% 7818 782 814
22832 Ephemeral 10 0.2% 4286 429 429
38502 Addressable 8 0.2% 3080 385 385
4004 Unknown 8 0.2% 9352 1169 1169
22785 Ephemeral 8 0.2% 3432 429 429
3 Follow list 8 0.2% 35850 4481 9845
1985 Label 8 0.2% 3792 474 474
22584 Ephemeral 8 0.2% 3416 427 427
22816 Ephemeral 8 0.2% 3432 429 429
22752 Ephemeral 8 0.2% 6212 776 3199
6 Repost 7 0.2% 10112 1445 1872
443 Unknown 7 0.2% 7780 1111 1116
22651 Ephemeral 6 0.1% 2568 428 428
1077 Unknown 6 0.1% 3642 607 607
26428 Ephemeral 6 0.1% 5254 876 876
22616 Ephemeral 6 0.1% 2574 429 429
22626 Ephemeral 6 0.1% 2568 428 428
22914 Ephemeral 6 0.1% 2580 430 430
22614 Ephemeral 6 0.1% 2568 428 428
22419 Ephemeral 5 0.1% 2218 444 444
22847 Ephemeral 5 0.1% 2155 431 431
30088 Addressable 5 0.1% 9317 1863 1871
10050 DM relays 5 0.1% 2250 450 450
30091 Addressable 5 0.1% 3085 617 629
30311 Addressable 5 0.1% 5153 1031 1197
23080 Ephemeral 5 0.1% 2155 431 431
38225 Addressable 4 0.1% 4077 1019 1036
4244 Unknown 4 0.1% 2256 564 564
26429 Ephemeral 4 0.1% 1964 491 491
3927 Unknown 4 0.1% 2132 533 533
22806 Ephemeral 4 0.1% 1712 428 428
30000 Follow sets 4 0.1% 9358 2340 2845
21617 Ephemeral 4 0.1% 1980 495 495
20333 Ephemeral 4 0.1% 3188 797 798
22548 Ephemeral 4 0.1% 1720 430 430
38503 Addressable 4 0.1% 2212 553 553
30515 Addressable 4 0.1% 18496 4624 4624
22740 Ephemeral 4 0.1% 1708 427 427
22226 Ephemeral 3 0.1% 1331 444 444
10011 Unknown 3 0.1% 1375 458 513
30089 Chunked data 3 0.1% 6944 2315 3412
30023 Long-form article 3 0.1% 19822 6607 7860
21060 Ephemeral 3 0.1% 1710 570 570
22526 Ephemeral 3 0.1% 1290 430 430
22511 Ephemeral 3 0.1% 2383 794 1163
22167 Ephemeral 3 0.1% 1331 444 444
30213 Addressable 3 0.1% 1866 622 632
22748 Ephemeral 3 0.1% 1284 428 428
31234 Addressable 3 0.1% 4248 1416 1416
22838 Ephemeral 2 0.0% 860 430 430
38500 Addressable 2 0.0% 1424 712 713
38384 Addressable 2 0.0% 1486 743 743
30443 Addressable 2 0.0% 2379 1190 1220
20384 Ephemeral 2 0.0% 1278 639 639
34237 Addressable 2 0.0% 836 418 418
22280 Ephemeral 2 0.0% 887 444 444
23194 NWC request 2 0.0% 1020 510 510
22320 Ephemeral 2 0.0% 887 444 444
30080 Addressable 2 0.0% 1971 986 986
22205 Ephemeral 2 0.0% 887 444 444
22770 Ephemeral 2 0.0% 862 431 431
22305 Ephemeral 2 0.0% 887 444 444
22356 Ephemeral 2 0.0% 886 443 443
22611 Ephemeral 2 0.0% 852 426 426
22217 Ephemeral 2 0.0% 887 444 444
22884 Ephemeral 2 0.0% 860 430 430
33334 Addressable 2 0.0% 1662 831 831
6300 Unknown 2 0.0% 8371 4186 5058
22169 Ephemeral 2 0.0% 887 444 444
22190 Ephemeral 2 0.0% 887 444 444
30079 Addressable 2 0.0% 1750 875 875
39101 Addressable 2 0.0% 1066 533 533
22345 Ephemeral 2 0.0% 887 444 444
22310 Ephemeral 2 0.0% 887 444 444
1111 Comment 2 0.0% 2557 1278 1564
22235 Ephemeral 2 0.0% 887 444 444
38385 Addressable 2 0.0% 2552 1276 1319
4747 Unknown 1 0.0% 721 721 721
22844 Ephemeral 1 0.0% 428 428 428
22807 Ephemeral 1 0.0% 3205 3205 3205
30066 Addressable 1 0.0% 675 675 675
4245 Unknown 1 0.0% 444 444 444
7376 Unknown 1 0.0% 521 521 521
22450 Ephemeral 1 0.0% 443 443 443
22261 Ephemeral 1 0.0% 443 443 443
4243 Unknown 1 0.0% 564 564 564
22242 Ephemeral 1 0.0% 443 443 443
10063 Unknown 1 0.0% 415 415 415
22306 Ephemeral 1 0.0% 443 443 443
22174 Ephemeral 1 0.0% 443 443 443
22831 Ephemeral 1 0.0% 3182 3182 3182
22227 Ephemeral 1 0.0% 443 443 443
7375 Unknown 1 0.0% 8629 8629 8629
22685 Ephemeral 1 0.0% 553 553 553
22299 Ephemeral 1 0.0% 443 443 443
22193 Ephemeral 1 0.0% 443 443 443
22975 Ephemeral 1 0.0% 3195 3195 3195
30380 Addressable 1 0.0% 454 454 454
20 Picture 1 0.0% 860 860 860
10030 Emoji list 1 0.0% 405 405 405
30087 Addressable 1 0.0% 7325 7325 7325
30090 Addressable 1 0.0% 1515 1515 1515
22220 Ephemeral 1 0.0% 443 443 443
39019 Addressable 1 0.0% 961 961 961
39015 Addressable 1 0.0% 696 696 696
30617 Addressable 1 0.0% 1089 1089 1089
30618 Addressable 1 0.0% 733 733 733
31991 Addressable 1 0.0% 461 461 461
1080 Unknown 1 0.0% 949 949 949
3434 Unknown 1 0.0% 705 705 705
33333 Addressable 1 0.0% 2346 2346 2346
22735 Ephemeral 1 0.0% 430 430 430
22415 Ephemeral 1 0.0% 443 443 443
21966 Ephemeral 1 0.0% 443 443 443
11316 Unknown 1 0.0% 725 725 725
11317 Unknown 1 0.0% 12051 12051 12051
25195 Ephemeral 1 0.0% 678 678 678
25196 Ephemeral 1 0.0% 1362 1362 1362
22402 Ephemeral 1 0.0% 443 443 443
31510 Addressable 1 0.0% 979 979 979
1078 Unknown 1 0.0% 14200 14200 14200
38383 Addressable 1 0.0% 685 685 685
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 09:38:02 2026
# Config: cache_all_test_config.jsonc
# Duration: 120 seconds
[2026-08-02 09:38:07] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 09:38:07] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 09:38:07] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 09:38:08] [RELAY] wss://relay.primal.net EOSE
@@ -0,0 +1,206 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 2m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 5012
Total bytes: 8091410 (7.72 MB)
Avg event size: 1614 bytes
Max event: 52672 bytes (kind 3)
Unique pubkeys: 2034
Relays contacted: 3
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 1599 1 0 0 0 0 OK
wss://nos.lol 2389 0 0 0 0 0 OK
wss://relay.damus.io 1024 0 0 0 0 0 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 1361 27.2% 4189338 3078 5314
5 Deletion 579 11.6% 254363 439 624
22668 Ephemeral 265 5.3% 113685 429 429
30078 App data 190 3.8% 702178 3696 14343
20001 Ephemeral (key xchg) 181 3.6% 108257 598 1489
22639 Ephemeral 176 3.5% 136058 773 1228
4515 Unknown 168 3.4% 319242 1900 14220
24133 Ephemeral 152 3.0% 464764 3058 14162
22734 Ephemeral 135 2.7% 57645 427 427
31991 Addressable 116 2.3% 53476 461 461
21050 Ephemeral 84 1.7% 81900 975 2558
1 Text note 80 1.6% 58412 730 1443
22795 Ephemeral 76 1.5% 104000 1368 1848
22850 Ephemeral 75 1.5% 33353 445 1169
38501 App data (btc) 69 1.4% 51105 741 744
22842 Ephemeral 55 1.1% 92475 1681 1849
10002 Relay list 48 1.0% 19784 412 537
0 Profile metadata 47 0.9% 34961 744 1356
37195 Addressable 38 0.8% 28214 742 771
7 Reaction 38 0.8% 21600 568 779
25050 Ephemeral 36 0.7% 15972 444 465
24242 Ephemeral 34 0.7% 19686 579 579
1059 Gift wrap seal 33 0.7% 58147 1762 3298
22484 Ephemeral 32 0.6% 29156 911 1164
22507 Ephemeral 28 0.6% 24308 868 1165
22613 Ephemeral 25 0.5% 17238 690 691
29333 Ephemeral 25 0.5% 12300 492 492
22601 Ephemeral 24 0.5% 10272 428 428
5300 Unknown 23 0.5% 14096 613 701
22751 Ephemeral 23 0.5% 9798 426 426
22699 Ephemeral 22 0.4% 9416 428 428
11998 Unknown 22 0.4% 10182 463 465
22676 Ephemeral 22 0.4% 9394 427 427
13194 NWC info 21 0.4% 10617 506 514
22774 Ephemeral 20 0.4% 8560 428 428
23003 Ephemeral 20 0.4% 8560 428 428
22655 Ephemeral 19 0.4% 8132 428 428
22543 Ephemeral 19 0.4% 34993 1842 1848
22455 Ephemeral 19 0.4% 34943 1839 1844
25555 Ephemeral 19 0.4% 94673 4983 17111
30443 Addressable 19 0.4% 22379 1178 1260
22777 Ephemeral 19 0.4% 16685 878 1651
22891 Ephemeral 18 0.4% 33189 1844 1848
31990 App handler info 17 0.3% 15825 931 1465
20000 Ephemeral 16 0.3% 7206 450 522
32123 Addressable 14 0.3% 10528 752 900
22456 Ephemeral 13 0.3% 6110 470 470
20004 Ephemeral 13 0.3% 6741 519 549
22591 Ephemeral 13 0.3% 6348 488 697
20787 Ephemeral 12 0.2% 16864 1405 2478
4 Encrypted DM 12 0.2% 7972 664 786
20387 Ephemeral 12 0.2% 6168 514 514
38502 Addressable 12 0.2% 4620 385 385
10100 Unknown 11 0.2% 20938 1903 3825
20050 Ephemeral 11 0.2% 9711 883 891
3 Follow list 10 0.2% 72137 7214 52672
9735 Zap receipt 10 0.2% 20833 2083 2143
445 Encrypted group msg 10 0.2% 17866 1787 4126
22626 Ephemeral 10 0.2% 4280 428 428
7368 Unknown 9 0.2% 9343 1038 1041
22736 Ephemeral 9 0.2% 3816 424 424
1985 Label 8 0.2% 3810 476 483
30091 Addressable 8 0.2% 4927 616 629
30315 User status 8 0.2% 4522 565 872
25055 Ephemeral 8 0.2% 10605 1326 1764
22791 Ephemeral 8 0.2% 3416 427 427
22816 Ephemeral 8 0.2% 3432 429 429
4004 Unknown 8 0.2% 9352 1169 1169
30088 Addressable 8 0.2% 14929 1866 1871
31234 Addressable 8 0.2% 14048 1756 1756
22813 Ephemeral 7 0.1% 2996 428 428
22957 Ephemeral 7 0.1% 3010 430 430
30089 Chunked data 7 0.1% 45972 6567 27988
32678 Addressable 7 0.1% 5704 815 900
22832 Ephemeral 6 0.1% 2574 429 429
22897 Ephemeral 6 0.1% 2586 431 431
22549 Ephemeral 6 0.1% 2574 429 429
22702 Ephemeral 6 0.1% 4753 792 2618
20333 Ephemeral 6 0.1% 4780 797 798
30023 Long-form article 6 0.1% 21101 3517 5056
22568 Ephemeral 5 0.1% 2135 427 427
38385 Addressable 5 0.1% 6849 1370 1496
4244 Unknown 5 0.1% 2820 564 564
22717 Ephemeral 5 0.1% 2135 427 427
21617 Ephemeral 4 0.1% 1980 495 495
38225 Addressable 4 0.1% 4061 1015 1039
6 Repost 4 0.1% 8823 2206 3647
22333 Ephemeral 4 0.1% 1773 443 444
22962 Ephemeral 3 0.1% 1290 430 430
33334 Addressable 3 0.1% 2229 743 831
1002 Unknown 3 0.1% 2793 931 979
22244 Ephemeral 3 0.1% 1330 443 444
22907 Ephemeral 3 0.1% 1287 429 429
10011 Unknown 3 0.1% 1229 410 431
22258 Ephemeral 3 0.1% 1330 443 444
22245 Ephemeral 3 0.1% 1331 444 444
30618 Addressable 3 0.1% 1755 585 733
22296 Ephemeral 3 0.1% 1331 444 444
38503 Addressable 3 0.1% 1659 553 553
22338 Ephemeral 3 0.1% 1330 443 444
22195 Ephemeral 3 0.1% 1331 444 444
22342 Ephemeral 3 0.1% 1331 444 444
10050 DM relays 3 0.1% 1350 450 450
30311 Addressable 3 0.1% 3414 1138 1197
32680 Addressable 2 0.0% 1376 688 688
30380 Addressable 2 0.0% 908 454 454
22611 Ephemeral 2 0.0% 852 426 426
4243 Unknown 2 0.0% 1128 564 564
22526 Ephemeral 2 0.0% 860 430 430
22320 Ephemeral 2 0.0% 887 444 444
4245 Unknown 2 0.0% 888 444 444
1984 Report 2 0.0% 873 436 446
1111 Comment 2 0.0% 3115 1558 1558
22311 Ephemeral 2 0.0% 886 443 443
22662 Ephemeral 2 0.0% 858 429 429
22089 Ephemeral 2 0.0% 886 443 443
22221 Ephemeral 2 0.0% 887 444 444
22157 Ephemeral 2 0.0% 887 444 444
38384 Addressable 2 0.0% 1486 743 743
20384 Ephemeral 2 0.0% 1278 639 639
30080 Addressable 2 0.0% 1679 840 985
39101 Addressable 2 0.0% 1066 533 533
6300 Unknown 2 0.0% 11904 5952 6898
10003 Bookmarks 2 0.0% 14466 7233 7233
30515 Addressable 2 0.0% 9248 4624 4624
22829 Ephemeral 2 0.0% 6387 3194 3194
22106 Ephemeral 2 0.0% 887 444 444
31927 Addressable 2 0.0% 3000 1500 1500
31510 Addressable 2 0.0% 1959 980 980
3927 Unknown 2 0.0% 1066 533 533
1040 Unknown 2 0.0% 2096 1048 1048
31000 Addressable 2 0.0% 1204 602 616
22201 Ephemeral 2 0.0% 886 443 443
22269 Ephemeral 2 0.0% 887 444 444
22818 Ephemeral 2 0.0% 852 426 426
22364 Ephemeral 2 0.0% 887 444 444
25195 Ephemeral 1 0.0% 678 678 678
25196 Ephemeral 1 0.0% 1362 1362 1362
22283 Ephemeral 1 0.0% 443 443 443
10030 Emoji list 1 0.0% 405 405 405
22706 Ephemeral 1 0.0% 3192 3192 3192
22236 Ephemeral 1 0.0% 737 737 737
31339 Addressable 1 0.0% 1470 1470 1470
22651 Ephemeral 1 0.0% 428 428 428
38500 Addressable 1 0.0% 713 713 713
10000 Mute list 1 0.0% 1311 1311 1311
31926 Addressable 1 0.0% 1038 1038 1038
22757 Ephemeral 1 0.0% 428 428 428
39015 Addressable 1 0.0% 696 696 696
30617 Addressable 1 0.0% 1089 1089 1089
22234 Ephemeral 1 0.0% 443 443 443
30815 Addressable 1 0.0% 37534 37534 37534
1078 Unknown 1 0.0% 13939 13939 13939
39050 Addressable 1 0.0% 1843 1843 1843
22281 Ephemeral 1 0.0% 443 443 443
30166 Addressable 1 0.0% 808 808 808
22772 Ephemeral 1 0.0% 429 429 429
22356 Ephemeral 1 0.0% 443 443 443
30554 Addressable 1 0.0% 609 609 609
38422 Addressable 1 0.0% 20025 20025 20025
38000 Addressable 1 0.0% 562 562 562
22069 Ephemeral 1 0.0% 443 443 443
1050 Unknown 1 0.0% 437 437 437
33051 Addressable 1 0.0% 522 522 522
39036 Addressable 1 0.0% 1138 1138 1138
23194 NWC request 1 0.0% 510 510 510
30081 Addressable 1 0.0% 1370 1370 1370
22299 Ephemeral 1 0.0% 443 443 443
22556 Ephemeral 1 0.0% 427 427 427
22302 Ephemeral 1 0.0% 443 443 443
39019 Addressable 1 0.0% 961 961 961
30000 Follow sets 1 0.0% 823 823 823
38383 Addressable 1 0.0% 657 657 657
30087 Addressable 1 0.0% 7325 7325 7325
30090 Addressable 1 0.0% 1515 1515 1515
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 09:48:55 2026
# Config: cache_all_test_config.jsonc
# Duration: 120 seconds
[2026-08-02 09:49:03] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 09:49:03] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 09:49:03] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 09:49:03] [RELAY] wss://relay.primal.net EOSE
@@ -0,0 +1,51 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 0m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 0
Total bytes: 0 (0.00 MB)
Avg event size: 0 bytes
Max event: 0 bytes (kind 0)
Unique pubkeys: 0
Relays contacted: 27
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 0 0 0 0 0 0 DOWN
wss://nos.lol 0 0 0 0 0 0 DOWN
wss://relay.damus.io 0 0 0 0 0 0 DOWN
wss://relay.ditto.pub 0 0 0 0 0 0 DOWN
wss://nostr.land 0 0 0 0 0 0 DOWN
wss://nostr.wine 0 0 0 0 0 0 DOWN
wss://nostr.oxtr.dev 0 0 0 0 0 0 DOWN
wss://spatia-arcana.com/inbox 0 0 0 0 0 0 DOWN
wss://eden.nostr.land 0 0 0 0 0 0 DOWN
wss://relay.nostr.band 0 0 0 0 0 0 DOWN
wss://theforest.nostr1.com 0 0 0 0 0 0 DOWN
wss://wot.utxo.one 0 0 0 0 0 0 DOWN
wss://premium.primal.net 0 0 0 0 0 0 DOWN
wss://nostr.mom 0 0 0 0 0 0 DOWN
wss://relay.mostr.pub 0 0 0 0 0 0 DOWN
wss://offchain.pub 0 0 0 0 0 0 DOWN
wss://relay.divine.video 0 0 0 0 0 0 DOWN
wss://relay.nostrplebs.com 0 0 0 0 0 0 DOWN
wss://nostr-pub.wellorder.net 0 0 0 0 0 0 DOWN
wss://devrelay.azzamo.net 0 0 0 0 0 0 DOWN
wss://relay.noderunners.network 0 0 0 0 0 0 DOWN
wss://nostr-pub.semisol.dev 0 0 0 0 0 0 DOWN
wss://relay.shosho.live 0 0 0 0 0 0 DOWN
wss://relay.nostrcheck.me 0 0 0 0 0 0 DOWN
wss://relay.nostr.info 0 0 0 0 0 0 DOWN
wss://relay.momostr.pink 0 0 0 0 0 0 DOWN
wss://bitsat.molonlabe.holdings 0 0 0 0 0 0 DOWN
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 10:01:51 2026
# Config: cache_all_test_config.jsonc
# Duration: 300 seconds
@@ -0,0 +1,39 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 0m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 0
Total bytes: 0 (0.00 MB)
Avg event size: 0 bytes
Max event: 0 bytes (kind 0)
Unique pubkeys: 0
Relays contacted: 15
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 0 0 0 0 0 0 DOWN
wss://nos.lol 0 0 0 0 0 0 DOWN
wss://relay.damus.io 0 0 0 0 0 0 DOWN
wss://nostr.land 0 0 0 0 0 0 DOWN
wss://relay.ditto.pub 0 0 0 0 0 0 DOWN
wss://nostr.oxtr.dev 0 0 0 0 0 0 DOWN
wss://pyramid.fiatjaf.com/inbox 0 0 0 0 0 0 DOWN
wss://relay.mostr.pub 0 0 0 0 0 0 DOWN
wss://relay.divine.video 0 0 0 0 0 0 DOWN
wss://nostr.wine 0 0 0 0 0 0 DOWN
wss://theforest.nostr1.com 0 0 0 0 0 0 DOWN
wss://relay.nostrplebs.com 0 0 0 0 0 0 DOWN
wss://relay.shosho.live 0 0 0 0 0 0 DOWN
wss://relay.nostr.band 0 0 0 0 0 0 DOWN
wss://relay.nostr.info 0 0 0 0 0 0 DOWN
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 10:10:55 2026
# Config: cache_all_test_config.jsonc
# Duration: 300 seconds
@@ -0,0 +1,35 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 0m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 0
Total bytes: 0 (0.00 MB)
Avg event size: 0 bytes
Max event: 0 bytes (kind 0)
Unique pubkeys: 0
Relays contacted: 11
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 0 0 0 0 0 0 DOWN
wss://nos.lol 0 0 0 0 0 0 DOWN
wss://relay.damus.io 0 0 0 0 0 0 DOWN
wss://relay.ditto.pub 0 0 0 0 0 0 DOWN
wss://nostr.bitcoiner.social 0 0 0 0 0 0 DOWN
wss://nostr.land 0 0 0 0 0 0 DOWN
wss://pyramid.fiatjaf.com/inbox 0 0 0 0 0 0 DOWN
wss://relay.divine.video 0 0 0 0 0 0 DOWN
wss://relay.nostrplebs.com 0 0 0 0 0 0 DOWN
wss://eden.nostr.land 0 0 0 0 0 0 DOWN
wss://relay.shosho.live 0 0 0 0 0 0 DOWN
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 10:17:20 2026
# Config: cache_all_test_config.jsonc
# Duration: 30 seconds
@@ -0,0 +1,197 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 1m 54s
=== Global Summary ===
Total events: 4719
Total bytes: 6608416 (6.30 MB)
Avg event size: 1400 bytes
Max event: 44372 bytes (kind 30089)
Unique pubkeys: 1333
Relays: 15
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://laantungir.net 0 0 0 0 0 0 DOWN
wss://nos.lol 804 0 0 0 0 3 OK
wss://nostr-pub.wellorder.net 83 0 0 0 0 0 OK
wss://nostr.mom 175 0 0 0 0 0 OK
wss://nostr.oxtr.dev 108 0 0 0 0 0 OK
wss://offchain.pub 627 0 0 0 0 0 OK
wss://premium.primal.net 1 0 0 0 0 0 OK
wss://relay.damus.io 0 0 0 0 0 0 DOWN
wss://relay.ditto.pub 721 0 0 0 0 0 OK
wss://relay.divine.video 3 0 0 0 0 0 OK
wss://relay.momostr.pink 86 0 0 0 0 0 OK
wss://relay.mostr.pub 836 0 0 0 0 0 OK
wss://relay.nostrplebs.com 4 0 0 0 0 0 OK
wss://relay.primal.net 1239 0 0 0 0 0 OK
wss://theforest.nostr1.com 32 0 0 0 0 0 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 675 14.3% 2192938 3249 5314
30382 Addressable 563 11.9% 255591 454 498
5 Deletion 341 7.2% 153667 451 1404
22639 Ephemeral 231 4.9% 178011 771 1230
22551 Ephemeral 182 3.9% 78260 430 430
20001 Ephemeral 143 3.0% 70427 492 1206
30078 App data 141 3.0% 1361122 9653 44169
0 Profile metadata 135 2.9% 119793 887 3483
22734 Ephemeral 122 2.6% 52094 427 427
24747 Ephemeral 101 2.1% 59352 588 594
1 Text note 90 1.9% 92140 1024 4709
22668 Ephemeral 88 1.9% 37752 429 429
22850 Ephemeral 83 1.8% 35690 430 430
22870 Ephemeral 76 1.6% 77061 1014 2202
22951 Ephemeral 75 1.6% 77282 1030 2204
10002 Relay list 75 1.6% 202196 2696 42385
22912 Ephemeral 74 1.6% 31672 428 428
38501 Addressable 65 1.4% 48152 741 744
24133 Ephemeral 63 1.3% 72262 1147 2898
6 Repost 50 1.1% 40985 820 3346
37195 Addressable 48 1.0% 36225 755 771
21050 Ephemeral 48 1.0% 46800 975 2558
4515 Unknown 46 1.0% 81638 1775 6028
25555 Ephemeral 45 1.0% 209551 4657 16603
7 Reaction 40 0.8% 22486 562 805
22917 Ephemeral 39 0.8% 35435 909 1166
22699 Ephemeral 39 0.8% 26723 685 1166
22690 Ephemeral 39 0.8% 16770 430 430
22918 Ephemeral 38 0.8% 36470 960 1507
22832 Ephemeral 37 0.8% 16128 436 446
25050 Ephemeral 35 0.7% 15507 443 465
30383 Addressable 35 0.7% 18766 536 541
22713 Ephemeral 34 0.7% 14570 429 430
22698 Ephemeral 34 0.7% 31371 923 1674
22601 Ephemeral 28 0.6% 11984 428 428
22655 Ephemeral 28 0.6% 11984 428 428
1059 Gift wrap seal 23 0.5% 46912 2040 5285
22751 Ephemeral 21 0.4% 8946 426 426
22676 Ephemeral 21 0.4% 8967 427 427
29333 Ephemeral 20 0.4% 9840 492 492
5300 Unknown 20 0.4% 11959 598 630
22743 Ephemeral 19 0.4% 8132 428 428
22603 Ephemeral 19 0.4% 8094 426 426
22693 Ephemeral 19 0.4% 8113 427 427
22774 Ephemeral 19 0.4% 8132 428 428
22808 Ephemeral 19 0.4% 8151 429 429
22575 Ephemeral 19 0.4% 8075 425 425
23003 Ephemeral 19 0.4% 8132 428 428
22949 Ephemeral 19 0.4% 8151 429 429
23334 Ephemeral 19 0.4% 8189 431 431
24242 Ephemeral 17 0.4% 9843 579 579
22816 Ephemeral 16 0.3% 6864 429 429
20787 Ephemeral 16 0.3% 25100 1569 2478
13194 NWC info 15 0.3% 7574 505 514
38502 Addressable 14 0.3% 5390 385 385
22236 Ephemeral 14 0.3% 11214 801 814
22613 Ephemeral 13 0.3% 8977 691 694
30088 Addressable 12 0.3% 22340 1862 1871
22484 Ephemeral 12 0.3% 10948 912 1165
30091 Addressable 12 0.3% 7385 615 629
20004 Ephemeral 11 0.2% 5643 513 549
31990 App handler info 11 0.2% 8711 792 1465
445 Encrypted group msg 10 0.2% 17972 1797 4126
22795 Ephemeral 9 0.2% 3861 429 429
22507 Ephemeral 9 0.2% 8480 942 1165
22922 Ephemeral 9 0.2% 3870 430 430
24421 Ephemeral 9 0.2% 4644 516 516
22830 Ephemeral 9 0.2% 7317 813 1168
20000 Ephemeral 9 0.2% 3854 428 484
22955 Ephemeral 9 0.2% 3852 428 428
22793 Ephemeral 9 0.2% 3843 427 427
30315 User status 8 0.2% 4604 576 926
20387 Ephemeral 8 0.2% 4112 514 514
7368 Unknown 8 0.2% 8309 1039 1041
4004 Unknown 8 0.2% 9352 1169 1169
443 Unknown 8 0.2% 8928 1116 1116
11998 Unknown 7 0.1% 3255 465 465
10100 Unknown 7 0.1% 11851 1693 3825
22456 Ephemeral 7 0.1% 3290 470 470
30089 Chunked data 7 0.1% 157322 22475 44372
22737 Ephemeral 7 0.1% 4669 667 707
23337 Ephemeral 7 0.1% 3557 508 509
30515 Addressable 7 0.1% 18032 2576 2576
30311 Addressable 6 0.1% 5818 970 1197
1985 Unknown 6 0.1% 2860 477 482
22859 Ephemeral 5 0.1% 8606 1721 2195
4 Encrypted DM 5 0.1% 3222 644 722
10011 Unknown 5 0.1% 2027 405 431
22764 Ephemeral 4 0.1% 1712 428 428
22777 Ephemeral 4 0.1% 2790 698 698
4244 Unknown 4 0.1% 2256 564 564
30023 Long-form article 4 0.1% 22789 5697 6817
9735 Zap receipt 4 0.1% 7033 1758 1805
20333 Ephemeral 4 0.1% 3188 797 798
3 Follow list 3 0.1% 26232 8744 14166
38503 Addressable 3 0.1% 1655 552 552
10050 DM relays 3 0.1% 1411 470 511
21617 Ephemeral 3 0.1% 1485 495 495
1984 Report 3 0.1% 1986 662 662
3927 Unknown 3 0.1% 1599 533 533
3434 Unknown 3 0.1% 1907 636 665
22514 Ephemeral 2 0.0% 856 428 428
33334 Addressable 2 0.0% 1662 831 831
38500 Addressable 2 0.0% 1424 712 713
22526 Ephemeral 2 0.0% 860 430 430
30380 Addressable 2 0.0% 908 454 454
23194 NWC request 2 0.0% 1020 510 510
23042 Ephemeral 2 0.0% 858 429 429
38385 Addressable 2 0.0% 2692 1346 1478
30443 Addressable 2 0.0% 2369 1184 1219
22747 Ephemeral 2 0.0% 2064 1032 1288
4245 Unknown 2 0.0% 888 444 444
1111 Comment 2 0.0% 1936 968 981
38383 Addressable 2 0.0% 1439 720 720
4243 Unknown 2 0.0% 1128 564 564
38225 Addressable 1 0.0% 978 978 978
30618 Addressable 1 0.0% 733 733 733
31510 Addressable 1 0.0% 981 981 981
30617 Addressable 1 0.0% 1089 1089 1089
1077 Unknown 1 0.0% 607 607 607
20201 Ephemeral 1 0.0% 491 491 491
20200 Ephemeral 1 0.0% 491 491 491
1078 Unknown 1 0.0% 14173 14173 14173
6300 Unknown 1 0.0% 3698 3698 3698
11316 Unknown 1 0.0% 791 791 791
11317 Unknown 1 0.0% 2873 2873 2873
17 Unknown 1 0.0% 610 610 610
23059 Ephemeral 1 0.0% 429 429 429
22806 Ephemeral 1 0.0% 1520 1520 1520
22120 Ephemeral 1 0.0% 1127 1127 1127
13196 Unknown 1 0.0% 656 656 656
24913 Ephemeral 1 0.0% 431 431 431
12222 Unknown 1 0.0% 579 579 579
1040 Unknown 1 0.0% 1048 1048 1048
22119 Ephemeral 1 0.0% 845 845 845
10312 Unknown 1 0.0% 472 472 472
3079 Unknown 1 0.0% 909 909 909
30000 Addressable 1 0.0% 594 594 594
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 10:25:47 2026
# Config: cache_all_test_config.jsonc
# Duration: 120 seconds
[2026-08-02 10:26:23] [RELAY] wss://laantungir.net STATUS: ?
[2026-08-02 10:26:23] [RELAY] wss://nos.lol STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://nostr-pub.wellorder.net STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://nostr.mom STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://nostr.oxtr.dev STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://offchain.pub STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://premium.primal.net STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://relay.damus.io STATUS: ?
[2026-08-02 10:26:23] [RELAY] wss://relay.ditto.pub STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://relay.divine.video STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://relay.momostr.pink STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://relay.mostr.pub STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://relay.nostrplebs.com STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://relay.primal.net STATUS: connected
[2026-08-02 10:26:23] [RELAY] wss://theforest.nostr1.com STATUS: connected
[2026-08-02 10:26:59] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 10:26:59] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 10:27:59] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 10:27:59] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 10:28:09] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 10:28:10] [RELAY] wss://nos.lol CONNECTED
@@ -0,0 +1,257 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 5m 0s
Config: cache_all_test_config.jsonc
=== Global Summary ===
Total events: 14714
Total bytes: 20163747 (19.23 MB)
Avg event size: 1370 bytes
Max event: 51791 bytes (kind 30078)
Unique pubkeys: 3435
Relays contacted: 24
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 2820 0 0 0 0 2 OK
wss://nos.lol 2964 0 0 0 0 3 OK
wss://relay.damus.io 1999 0 0 0 0 0 OK
wss://relay.ditto.pub 2049 0 0 0 0 0 OK
wss://nostr.land 0 0 0 0 0 0 DOWN
wss://nostr.wine 0 0 0 0 0 0 DOWN
wss://nostr.oxtr.dev 599 0 0 0 0 1 OK
wss://spatia-arcana.com/inbox 0 0 0 0 0 0 DOWN
wss://nostrelites.org 0 0 0 0 0 0 DOWN
wss://offchain.pub 1431 0 0 0 0 0 OK
wss://relay.nostr.band 0 0 0 0 0 0 DOWN
wss://premium.primal.net 7 0 0 0 0 0 OK
wss://nostr.mom 412 0 0 0 0 0 OK
wss://relay.mostr.pub 2134 0 0 0 0 1 OK
wss://relay.divine.video 14 0 0 0 0 0 OK
wss://relay.nostrplebs.com 4 0 0 0 0 0 OK
wss://eden.nostr.land 0 0 0 0 0 0 DOWN
wss://theforest.nostr1.com 67 0 0 0 0 0 OK
wss://nostr-pub.wellorder.net 214 0 0 0 0 0 OK
wss://devrelay.azzamo.net 0 0 0 0 0 0 DOWN
wss://relay.noderunners.network 0 0 0 0 0 0 OK
wss://relay.shosho.live 0 0 0 0 0 0 DOWN
wss://relay.nostrcheck.me 0 0 0 0 0 0 DOWN
wss://basspistol.org 0 0 0 0 0 0 DOWN
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 2477 16.8% 8220486 3319 19622
30382 Addressable 1558 10.6% 708564 455 498
5 Deletion 1219 8.3% 542263 445 1404
22639 Ephemeral 607 4.1% 467729 771 1230
20001 Ephemeral (key xchg) 519 3.5% 251716 485 1431
22551 Ephemeral 479 3.3% 205970 430 430
30078 App data 432 2.9% 3024405 7001 51791
24747 Ephemeral 317 2.2% 188378 594 650
22734 Ephemeral 315 2.1% 134505 427 427
22668 Ephemeral 307 2.1% 131703 429 429
1 Text note 307 2.1% 270378 881 3665
0 Profile metadata 300 2.0% 260832 869 9187
22850 Ephemeral 242 1.6% 104060 430 430
38501 App data (btc) 241 1.6% 178603 741 744
22912 Ephemeral 238 1.6% 101864 428 428
4515 Unknown 225 1.5% 376537 1673 6028
10002 Relay list 211 1.4% 309808 1468 42385
24133 Ephemeral 193 1.3% 237962 1233 14162
22951 Ephemeral 179 1.2% 170939 955 2204
22870 Ephemeral 172 1.2% 165571 963 2202
25555 Ephemeral 157 1.1% 776467 4946 16603
37195 Addressable 142 1.0% 107593 758 869
21050 Ephemeral 141 1.0% 136906 971 2558
6 Repost 139 0.9% 113883 819 4053
7 Reaction 116 0.8% 65547 565 786
22832 Ephemeral 112 0.8% 48745 435 446
22690 Ephemeral 109 0.7% 46870 430 430
30383 Addressable 100 0.7% 53571 536 543
22713 Ephemeral 98 0.7% 41994 429 430
22601 Ephemeral 93 0.6% 39804 428 428
25050 Ephemeral 87 0.6% 38343 441 465
1059 Gift wrap seal 84 0.6% 158090 1882 5285
22699 Ephemeral 83 0.6% 55625 670 1872
22655 Ephemeral 80 0.5% 34240 428 428
22917 Ephemeral 75 0.5% 67279 897 1166
22676 Ephemeral 72 0.5% 30744 427 427
29333 Ephemeral 70 0.5% 34440 492 492
22918 Ephemeral 67 0.5% 65746 981 1876
20787 Ephemeral 62 0.4% 84460 1362 2478
22456 Ephemeral 61 0.4% 29310 480 790
22808 Ephemeral 59 0.4% 25296 429 429
24242 Ephemeral 57 0.4% 33003 579 579
22816 Ephemeral 56 0.4% 24024 429 429
22603 Ephemeral 55 0.4% 23430 426 426
22743 Ephemeral 55 0.4% 23540 428 428
22693 Ephemeral 54 0.4% 23058 427 427
22575 Ephemeral 54 0.4% 22950 425 425
23334 Ephemeral 53 0.4% 22843 431 431
13194 NWC info 52 0.4% 26592 511 514
22751 Ephemeral 51 0.3% 21726 426 426
22949 Ephemeral 50 0.3% 21450 429 429
11998 Unknown 50 0.3% 23202 464 465
24421 Ephemeral 48 0.3% 24795 517 525
22236 Ephemeral 47 0.3% 37103 789 814
22484 Ephemeral 47 0.3% 42919 913 1165
3927 Unknown 45 0.3% 23985 533 533
23003 Ephemeral 45 0.3% 19260 428 428
22774 Ephemeral 45 0.3% 19260 428 428
31990 App handler info 44 0.3% 40657 924 1465
5300 Unknown 43 0.3% 25958 604 701
20387 Ephemeral 40 0.3% 20560 514 514
20000 Ephemeral 38 0.3% 15672 412 487
38502 Addressable 38 0.3% 14630 385 385
22507 Ephemeral 38 0.3% 34749 914 1167
445 Encrypted group msg 37 0.3% 62402 1687 4130
22698 Ephemeral 34 0.2% 31371 923 1674
4 Encrypted DM 33 0.2% 44743 1356 17018
30315 User status 31 0.2% 20374 657 1166
1985 Label 31 0.2% 28905 932 4831
22859 Ephemeral 31 0.2% 30700 990 2195
22613 Ephemeral 31 0.2% 21415 691 694
20004 Ephemeral 29 0.2% 15525 535 549
7368 Unknown 27 0.2% 28042 1039 1041
30091 Addressable 27 0.2% 16620 616 629
30088 Addressable 27 0.2% 50292 1863 1871
22598 Ephemeral 27 0.2% 23198 859 1162
22955 Ephemeral 25 0.2% 10700 428 428
4004 Unknown 25 0.2% 29225 1169 1169
22793 Ephemeral 25 0.2% 10675 427 427
22922 Ephemeral 25 0.2% 10750 430 430
22795 Ephemeral 25 0.2% 10725 429 429
10100 Unknown 24 0.2% 39530 1647 3825
22764 Ephemeral 22 0.1% 9416 428 428
30089 Chunked data 18 0.1% 260436 14469 44372
30311 Addressable 18 0.1% 18058 1003 1197
20333 Ephemeral 18 0.1% 14340 797 798
443 Unknown 18 0.1% 20088 1116 1116
38383 Addressable 18 0.1% 13557 753 972
22777 Ephemeral 16 0.1% 12435 777 1159
30023 Long-form article 16 0.1% 92998 5812 8050
4244 Unknown 15 0.1% 8460 564 564
30515 Addressable 14 0.1% 36064 2576 2576
22967 Ephemeral 13 0.1% 5603 431 431
10011 Unknown 13 0.1% 5283 406 431
3 Follow list 12 0.1% 97388 8116 24970
38503 Addressable 12 0.1% 6624 552 553
1077 Unknown 12 0.1% 7284 607 607
22550 Ephemeral 12 0.1% 5112 426 426
9735 Zap receipt 12 0.1% 21551 1796 1963
22591 Ephemeral 11 0.1% 4686 426 426
22830 Ephemeral 11 0.1% 8707 792 1168
23337 Ephemeral 11 0.1% 5589 508 509
38385 Addressable 9 0.1% 12290 1366 1478
10050 DM relays 9 0.1% 4160 462 511
39101 Addressable 8 0.1% 4264 533 533
31510 Addressable 8 0.1% 7834 979 982
1111 Comment 8 0.1% 9909 1239 1706
1002 Unknown 7 0.0% 6647 950 1009
22737 Ephemeral 7 0.0% 4669 667 707
33334 Addressable 6 0.0% 4986 831 831
38384 Addressable 6 0.0% 4458 743 743
20384 Ephemeral 6 0.0% 3834 639 639
22526 Ephemeral 6 0.0% 2580 430 430
30443 Addressable 6 0.0% 7288 1215 1260
3434 Unknown 6 0.0% 3902 650 665
21617 Ephemeral 6 0.0% 2970 495 495
38500 Addressable 6 0.0% 4272 712 713
30380 Addressable 6 0.0% 2724 454 454
22514 Ephemeral 5 0.0% 2140 428 428
4245 Unknown 5 0.0% 2220 444 444
4243 Unknown 5 0.0% 2820 564 564
6300 Unknown 5 0.0% 19347 3869 4596
30000 Follow sets 5 0.0% 3005 601 823
22611 Ephemeral 5 0.0% 2130 426 426
38225 Addressable 5 0.0% 4929 986 993
23042 Ephemeral 5 0.0% 2145 429 429
14 Unknown 4 0.0% 5446 1362 1550
23059 Ephemeral 4 0.0% 2853 713 1162
3079 Unknown 4 0.0% 3636 909 909
1984 Report 4 0.0% 2648 662 662
23194 NWC request 4 0.0% 2040 510 510
21060 Ephemeral 4 0.0% 2280 570 570
30080 Addressable 4 0.0% 3763 941 986
31234 Addressable 4 0.0% 4512 1128 1856
10003 Bookmarks 3 0.0% 10518 3506 3506
1632 Unknown 3 0.0% 2187 729 729
22120 Ephemeral 3 0.0% 3229 1076 1127
1000 Unknown 3 0.0% 5178 1726 1734
22119 Ephemeral 3 0.0% 2537 846 846
11317 Unknown 2 0.0% 5746 2873 2873
11316 Unknown 2 0.0% 1582 791 791
30617 Addressable 2 0.0% 2178 1089 1089
30618 Addressable 2 0.0% 1466 733 733
17 External reaction 2 0.0% 1220 610 610
31991 Addressable 2 0.0% 922 461 461
24913 Ephemeral 2 0.0% 862 431 431
31985 Addressable 2 0.0% 1267 634 667
1060 Gift wrap (alt) 2 0.0% 2268 1134 1134
22806 Ephemeral 2 0.0% 3040 1520 1520
31339 Addressable 2 0.0% 2940 1470 1470
30090 Addressable 2 0.0% 3030 1515 1515
10000 Mute list 2 0.0% 1963 982 1034
30087 Addressable 2 0.0% 14650 7325 7325
10312 Unknown 2 0.0% 963 482 491
30815 Addressable 2 0.0% 75068 37534 37534
20200 Ephemeral 2 0.0% 982 491 491
20201 Ephemeral 2 0.0% 982 491 491
22747 Ephemeral 2 0.0% 2064 1032 1288
30787 Addressable 1 0.0% 1370 1370 1370
1078 Unknown 1 0.0% 14173 14173 14173
13196 Unknown 1 0.0% 656 656 656
7403 Unknown 1 0.0% 491 491 491
15129 Unknown 1 0.0% 455 455 455
12222 Unknown 1 0.0% 579 579 579
10030 Emoji list 1 0.0% 405 405 405
38111 Addressable 1 0.0% 748 748 748
15750 Unknown 1 0.0% 616 616 616
30421 Addressable 1 0.0% 995 995 995
33333 Addressable 1 0.0% 2346 2346 2346
30079 Addressable 1 0.0% 875 875 875
39019 Addressable 1 0.0% 961 961 961
39015 Addressable 1 0.0% 696 696 696
5555 Unknown 1 0.0% 732 732 732
34235 Addressable 1 0.0% 1071 1071 1071
1040 Unknown 1 0.0% 1048 1048 1048
31000 Addressable 1 0.0% 616 616 616
10063 Unknown 1 0.0% 627 627 627
30554 Addressable 1 0.0% 609 609 609
25195 Ephemeral 1 0.0% 678 678 678
25196 Ephemeral 1 0.0% 1362 1362 1362
30213 Addressable 1 0.0% 632 632 632
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 10:18:44 2026
# Config: cache_all_test_config.jsonc
# Duration: 300 seconds
[2026-08-02 10:25:43] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://offchain.pub CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://nostr.mom CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://relay.divine.video CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://relay.nostrplebs.com CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://theforest.nostr1.com CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://nostr-pub.wellorder.net CONNECTED
[2026-08-02 10:25:43] [RELAY] wss://relay.noderunners.network CONNECTED
[2026-08-02 10:26:30] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 10:26:30] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 10:27:00] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 10:27:00] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 10:28:30] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 10:28:30] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 10:28:55] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 10:28:55] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 10:29:49] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 10:29:50] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 10:29:50] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 10:29:51] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 10:30:00] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 10:30:00] [RELAY] wss://nos.lol CONNECTED
@@ -0,0 +1,167 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 0m 32s
=== Global Summary ===
Total events: 1485
Total bytes: 1812166 (1.73 MB)
Avg event size: 1220 bytes
Max event: 44546 bytes (kind 31234)
Unique pubkeys: 511
Relays: 15
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://laantungir.net 0 0 0 0 0 0 DOWN
wss://nos.lol 247 0 0 0 0 0 OK
wss://nostr-pub.wellorder.net 37 0 0 0 0 0 OK
wss://nostr.mom 38 0 0 0 0 0 OK
wss://nostr.oxtr.dev 36 0 0 0 0 0 OK
wss://offchain.pub 113 0 0 0 0 0 OK
wss://premium.primal.net 1 0 0 0 0 0 OK
wss://relay.damus.io 194 0 0 0 0 0 OK
wss://relay.ditto.pub 277 0 0 0 0 1 OK
wss://relay.divine.video 2 0 0 0 0 0 OK
wss://relay.momostr.pink 27 0 0 0 0 0 OK
wss://relay.mostr.pub 148 0 0 0 0 0 OK
wss://relay.nostrplebs.com 0 0 0 0 0 0 OK
wss://relay.primal.net 360 0 0 0 0 0 OK
wss://theforest.nostr1.com 5 0 0 0 0 0 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 184 12.4% 623532 3389 5314
22591 Ephemeral 139 9.4% 107560 774 1223
30382 Addressable 119 8.0% 53663 451 458
30383 Addressable 119 8.0% 53605 450 536
5 Deletion 91 6.1% 39950 439 624
30078 App data 54 3.6% 179695 3328 14343
20001 Ephemeral 49 3.3% 35635 727 1871
0 Profile metadata 48 3.2% 39906 831 1468
22668 Ephemeral 48 3.2% 20592 429 429
1 Text note 45 3.0% 38547 857 2413
22734 Ephemeral 26 1.8% 11102 427 427
4515 Unknown 26 1.8% 38218 1470 2956
10002 Relay list 25 1.7% 11707 468 565
22850 Ephemeral 24 1.6% 10320 430 430
7 Reaction 24 1.6% 13528 564 780
22551 Ephemeral 24 1.6% 10320 430 430
31991 Addressable 20 1.3% 9220 461 461
38501 Addressable 18 1.2% 13341 741 744
22663 Ephemeral 17 1.1% 7276 428 428
25555 Ephemeral 15 1.0% 90493 6033 16887
22912 Ephemeral 13 0.9% 5564 428 428
1059 Gift wrap seal 13 0.9% 23775 1829 1883
22832 Ephemeral 12 0.8% 5233 436 446
22690 Ephemeral 11 0.7% 4730 430 430
22545 Ephemeral 11 0.7% 4697 427 427
25050 Ephemeral 10 0.7% 4394 439 465
37195 Addressable 10 0.7% 7494 749 771
22601 Ephemeral 10 0.7% 4280 428 428
22795 Ephemeral 10 0.7% 11639 1164 1487
22891 Ephemeral 9 0.6% 13331 1481 1490
22655 Ephemeral 9 0.6% 3852 428 428
22911 Ephemeral 9 0.6% 13328 1481 1487
24133 Ephemeral 9 0.6% 8250 917 1534
13194 NWC info 8 0.5% 4112 514 514
22647 Ephemeral 8 0.5% 3416 427 427
22695 Ephemeral 7 0.5% 10339 1477 1481
22653 Ephemeral 7 0.5% 10368 1481 1487
22455 Ephemeral 7 0.5% 10339 1477 1485
22543 Ephemeral 7 0.5% 10354 1479 1487
30315 User status 7 0.5% 2714 388 398
22676 Ephemeral 6 0.4% 2562 427 427
22575 Ephemeral 6 0.4% 2550 425 425
22689 Ephemeral 6 0.4% 8851 1475 1484
22913 Ephemeral 6 0.4% 2586 431 431
22774 Ephemeral 6 0.4% 2568 428 428
24242 Ephemeral 6 0.4% 3474 579 579
22808 Ephemeral 6 0.4% 2574 429 429
22842 Ephemeral 6 0.4% 8891 1482 1488
22603 Ephemeral 5 0.3% 2130 426 426
6 Repost 5 0.3% 10814 2163 7298
31990 App handler info 5 0.3% 4305 861 1321
22384 Ephemeral 5 0.3% 2130 426 426
22699 Ephemeral 5 0.3% 2140 428 428
22613 Ephemeral 5 0.3% 3443 689 691
38502 Addressable 5 0.3% 1925 385 385
23003 Ephemeral 5 0.3% 2140 428 428
22743 Ephemeral 5 0.3% 2140 428 428
29333 Ephemeral 5 0.3% 2460 492 492
20004 Ephemeral 4 0.3% 1800 450 549
4 Encrypted DM 4 0.3% 3072 768 826
20387 Ephemeral 4 0.3% 2056 514 514
22816 Ephemeral 4 0.3% 1716 429 429
20000 Ephemeral 4 0.3% 1872 468 628
22934 Ephemeral 3 0.2% 5535 1845 1846
1985 Unknown 2 0.1% 948 474 474
31234 Addressable 2 0.1% 89092 44546 44546
10777 Unknown 2 0.1% 976 488 488
4004 Unknown 2 0.1% 2338 1169 1169
9735 Zap receipt 2 0.1% 3926 1963 2135
1000 Unknown 2 0.1% 3415 1708 1712
25195 Ephemeral 2 0.1% 1356 678 678
30311 Addressable 2 0.1% 2210 1105 1198
25196 Ephemeral 2 0.1% 3576 1788 2214
1040 Unknown 2 0.1% 1920 960 960
38383 Addressable 2 0.1% 1532 766 815
30515 Addressable 2 0.1% 5152 2576 2576
30091 Addressable 2 0.1% 1221 610 611
30088 Addressable 2 0.1% 3717 1858 1859
21617 Ephemeral 1 0.1% 494 494 494
30380 Addressable 1 0.1% 454 454 454
22611 Ephemeral 1 0.1% 426 426 426
30079 Addressable 1 0.1% 875 875 875
22767 Ephemeral 1 0.1% 429 429 429
38500 Addressable 1 0.1% 711 711 711
30312 Addressable 1 0.1% 461 461 461
7368 Unknown 1 0.1% 1039 1039 1039
22967 Ephemeral 1 0.1% 431 431 431
39101 Addressable 1 0.1% 533 533 533
445 Encrypted group msg 1 0.1% 4130 4130 4130
10000 Mute list 1 0.1% 1722 1722 1722
30617 Addressable 1 0.1% 1089 1089 1089
22526 Ephemeral 1 0.1% 430 430 430
20787 Ephemeral 1 0.1% 926 926 926
443 Unknown 1 0.1% 800 800 800
16 Generic repost 1 0.1% 780 780 780
10011 Unknown 1 0.1% 397 397 397
22679 Ephemeral 1 0.1% 693 693 693
23042 Ephemeral 1 0.1% 429 429 429
22514 Ephemeral 1 0.1% 428 428 428
30023 Long-form article 1 0.1% 8164 8164 8164
5300 Unknown 1 0.1% 630 630 630
22605 Ephemeral 1 0.1% 692 692 692
34237 Addressable 1 0.1% 418 418 418
30618 Addressable 1 0.1% 733 733 733
38421 Addressable 1 0.1% 1069 1069 1069
10100 Unknown 1 0.1% 1036 1036 1036
30080 Addressable 1 0.1% 925 925 925
38385 Addressable 1 0.1% 1214 1214 1214
38225 Addressable 1 0.1% 1012 1012 1012
11998 Unknown 1 0.1% 465 465 465
22236 Ephemeral 1 0.1% 747 747 747
22823 Ephemeral 1 0.1% 1852 1852 1852
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 11:20:28 2026
# Config: cache_all_test_config.jsonc
# Duration: 86400 seconds
[2026-08-02 11:21:06] [RELAY] wss://laantungir.net STATUS: ?
[2026-08-02 11:21:06] [RELAY] wss://nos.lol STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://nostr-pub.wellorder.net STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://nostr.mom STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://nostr.oxtr.dev STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://offchain.pub STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://premium.primal.net STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.damus.io STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.ditto.pub STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.divine.video STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.momostr.pink STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.mostr.pub STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.nostrplebs.com STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://relay.primal.net STATUS: connected
[2026-08-02 11:21:06] [RELAY] wss://theforest.nostr1.com STATUS: connected
[2026-08-02 11:21:26] [RELAY] wss://relay.ditto.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:21:26] [RELAY] wss://relay.ditto.pub CONNECTED
@@ -0,0 +1,423 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 40m 5s
=== Global Summary ===
Total events: 40082
Total bytes: 52985157 (50.53 MB)
Avg event size: 1322 bytes
Max event: 58662 bytes (kind 3)
Unique pubkeys: 7202
Relays: 15
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://laantungir.net 0 0 0 0 0 0 DOWN
wss://nos.lol 7469 0 0 0 0 11 OK
wss://nostr-pub.wellorder.net 697 0 0 0 0 0 OK
wss://nostr.mom 1389 0 0 0 0 0 OK
wss://nostr.oxtr.dev 1009 0 0 0 0 0 OK
wss://offchain.pub 4338 0 0 0 0 0 OK
wss://premium.primal.net 226 0 0 0 0 36 OK
wss://relay.damus.io 4929 0 0 0 0 6 OK
wss://relay.ditto.pub 5951 0 0 0 0 0 OK
wss://relay.divine.video 127 0 0 0 0 0 OK
wss://relay.momostr.pink 857 0 0 0 0 0 OK
wss://relay.mostr.pub 4182 0 0 0 0 1 OK
wss://relay.nostrplebs.com 23 0 0 0 0 1 OK
wss://relay.primal.net 8596 0 0 0 0 10 OK
wss://theforest.nostr1.com 289 0 0 0 0 0 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 5631 14.0% 18980562 3371 5314
30382 Addressable 4179 10.4% 1900908 455 483
22591 Ephemeral 3320 8.3% 2581724 778 1814
5 Deletion 2848 7.1% 1264487 444 1659
20001 Ephemeral 1602 4.0% 1333680 833 5870
1 Text note 1556 3.9% 1359052 873 2843
30078 App data 1118 2.8% 4202383 3759 52067
22668 Ephemeral 1014 2.5% 435006 429 429
24133 Ephemeral 965 2.4% 2468722 2558 19622
0 Profile metadata 959 2.4% 865512 903 6506
7 Reaction 766 1.9% 465618 608 1403
22551 Ephemeral 703 1.8% 305982 435 1819
22734 Ephemeral 684 1.7% 292068 427 427
38501 Addressable 583 1.5% 432057 741 744
22912 Ephemeral 582 1.5% 249096 428 428
10002 Relay list 579 1.4% 898767 1552 42385
22850 Ephemeral 543 1.4% 233490 430 430
37195 Addressable 436 1.1% 329238 755 934
25555 Ephemeral 407 1.0% 2047337 5030 16903
30383 Addressable 379 0.9% 197360 521 544
22832 Ephemeral 342 0.9% 149404 437 446
22690 Ephemeral 309 0.8% 132870 430 430
22651 Ephemeral 305 0.8% 130235 427 427
22236 Ephemeral 301 0.8% 226221 752 815
6 Repost 299 0.7% 301866 1010 9866
4515 Unknown 291 0.7% 533897 1835 16952
22601 Ephemeral 252 0.6% 107856 428 428
25050 Ephemeral 233 0.6% 102329 439 465
31991 Addressable 229 0.6% 105569 461 461
22655 Ephemeral 218 0.5% 93304 428 428
29333 Ephemeral 215 0.5% 105780 492 492
22795 Ephemeral 188 0.5% 214828 1143 1847
22676 Ephemeral 188 0.5% 80276 427 427
22808 Ephemeral 178 0.4% 84072 472 1253
22384 Ephemeral 175 0.4% 74550 426 426
31990 App handler info 167 0.4% 183247 1097 6861
22913 Ephemeral 160 0.4% 68960 431 431
13194 NWC info 156 0.4% 80184 514 514
22603 Ephemeral 155 0.4% 66030 426 426
22743 Ephemeral 155 0.4% 66340 428 428
22575 Ephemeral 154 0.4% 65450 425 425
20000 Ephemeral 151 0.4% 67354 446 1198
7368 Unknown 149 0.4% 154743 1039 1059
23049 Ephemeral 147 0.4% 63210 430 430
24242 Ephemeral 144 0.4% 83376 579 579
11998 Unknown 142 0.4% 65790 463 465
445 Encrypted group msg 140 0.3% 293740 2098 5194
22456 Ephemeral 131 0.3% 63614 486 790
20787 Ephemeral 131 0.3% 179206 1368 2478
20387 Ephemeral 128 0.3% 65792 514 514
22911 Ephemeral 125 0.3% 190126 1521 1849
22653 Ephemeral 124 0.3% 188585 1521 1848
22455 Ephemeral 124 0.3% 188540 1520 1844
22695 Ephemeral 124 0.3% 188335 1519 1850
22891 Ephemeral 122 0.3% 185731 1522 1850
1059 Gift wrap seal 122 0.3% 235480 1930 5285
22543 Ephemeral 122 0.3% 185791 1523 1846
22689 Ephemeral 119 0.3% 181177 1522 1845
22816 Ephemeral 118 0.3% 50622 429 429
22774 Ephemeral 115 0.3% 49220 428 428
23003 Ephemeral 113 0.3% 48364 428 428
22842 Ephemeral 111 0.3% 169484 1527 1848
443 Unknown 111 0.3% 163453 1473 22480
23337 Ephemeral 108 0.3% 57242 530 531
30315 User status 107 0.3% 66269 619 1166
22699 Ephemeral 106 0.3% 48256 455 1505
1985 Unknown 104 0.3% 63816 614 4833
22663 Ephemeral 101 0.3% 43228 428 428
38502 Addressable 101 0.3% 38885 385 385
23004 Ephemeral 100 0.2% 43100 431 431
4004 Unknown 100 0.2% 116900 1169 1169
3 Follow list 96 0.2% 1944563 20256 58662
4 Encrypted DM 95 0.2% 95818 1009 17018
9735 Zap receipt 94 0.2% 183437 1951 2436
22647 Ephemeral 83 0.2% 35441 427 427
31234 Addressable 81 0.2% 177493 2191 44546
5300 Unknown 80 0.2% 48788 610 744
23334 Ephemeral 75 0.2% 33681 449 454
10100 Unknown 74 0.2% 110084 1488 3826
22613 Ephemeral 74 0.2% 53289 720 1814
20004 Ephemeral 71 0.2% 37593 529 549
22631 Ephemeral 67 0.2% 61813 923 1822
20333 Ephemeral 67 0.2% 53378 797 798
22657 Ephemeral 67 0.2% 53820 803 1279
10050 DM relays 64 0.2% 27983 437 536
22545 Ephemeral 61 0.2% 26047 427 427
30023 Long-form article 56 0.1% 369281 6594 16772
30311 Addressable 55 0.1% 56530 1028 1198
1111 Comment 51 0.1% 72307 1418 2352
30166 Addressable 49 0.1% 26188 534 808
4244 Unknown 41 0.1% 23124 564 564
30088 Addressable 41 0.1% 76377 1863 1871
30091 Addressable 40 0.1% 24624 616 629
38503 Addressable 40 0.1% 22081 552 553
22782 Ephemeral 40 0.1% 36524 913 1167
22611 Ephemeral 38 0.1% 16263 428 429
22605 Ephemeral 36 0.1% 42806 1189 1849
22767 Ephemeral 35 0.1% 15015 429 429
22967 Ephemeral 34 0.1% 14654 431 431
38383 Addressable 33 0.1% 28002 849 3207
10011 Unknown 32 0.1% 13152 411 431
8108 Unknown 30 0.1% 39052 1302 1574
22623 Ephemeral 30 0.1% 24536 818 1283
30089 Chunked data 29 0.1% 370012 12759 44372
30515 Addressable 29 0.1% 74704 2576 2576
10777 Unknown 26 0.1% 12688 488 488
38385 Addressable 26 0.1% 34815 1339 1496
22679 Ephemeral 24 0.1% 28787 1199 1852
25195 Ephemeral 24 0.1% 14928 622 678
21060 Ephemeral 23 0.1% 13110 570 570
22738 Ephemeral 23 0.1% 18515 805 1252
30380 Addressable 23 0.1% 10442 454 454
33334 Addressable 22 0.1% 18282 831 831
22547 Ephemeral 22 0.1% 20278 922 1164
21617 Ephemeral 21 0.1% 10544 502 517
33301 Addressable 20 0.0% 35736 1787 2162
38500 Addressable 19 0.0% 13531 712 713
38384 Addressable 19 0.0% 14115 743 743
22526 Ephemeral 19 0.0% 8170 430 430
6300 Unknown 19 0.0% 64041 3371 6898
30443 Addressable 19 0.0% 22604 1190 1260
22919 Ephemeral 18 0.0% 32466 1804 1849
22724 Ephemeral 18 0.0% 13222 735 1253
22789 Ephemeral 17 0.0% 28521 1678 3267
1002 Unknown 17 0.0% 16010 942 981
3434 Unknown 17 0.0% 11585 681 705
22507 Ephemeral 16 0.0% 9927 620 1163
20384 Ephemeral 16 0.0% 10223 639 639
30080 Addressable 16 0.0% 15048 940 987
22672 Ephemeral 16 0.0% 24987 1562 1847
22844 Ephemeral 15 0.0% 6435 429 429
4245 Unknown 15 0.0% 6660 444 444
4243 Unknown 15 0.0% 8460 564 564
30009 Addressable 15 0.0% 6949 463 479
22514 Ephemeral 14 0.0% 5992 428 428
14 Unknown 14 0.0% 15322 1094 1551
3927 Unknown 14 0.0% 7484 535 544
23042 Ephemeral 14 0.0% 6006 429 429
10000 Mute list 14 0.0% 32097 2293 8671
30618 Addressable 13 0.0% 25947 1996 8739
1040 Unknown 13 0.0% 13272 1021 1048
31510 Addressable 13 0.0% 12731 979 982
30617 Addressable 12 0.0% 13112 1093 1146
25196 Ephemeral 12 0.0% 19580 1632 2214
38225 Addressable 12 0.0% 11742 978 1006
39101 Addressable 11 0.0% 5863 533 533
26428 Ephemeral 10 0.0% 8765 876 877
30213 Addressable 10 0.0% 6224 622 632
26429 Ephemeral 10 0.0% 4910 491 491
30000 Addressable 9 0.0% 17509 1945 5660
22639 Ephemeral 9 0.0% 3852 428 428
31339 Addressable 9 0.0% 13230 1470 1470
30312 Addressable 8 0.0% 3555 444 461
9321 Unknown 7 0.0% 16541 2363 2363
7403 Unknown 7 0.0% 3435 491 491
1010 Unknown 7 0.0% 5098 728 748
16 Generic repost 7 0.0% 5460 780 780
7375 Unknown 7 0.0% 8120 1160 1160
22839 Ephemeral 7 0.0% 2996 428 428
7376 Unknown 7 0.0% 5817 831 831
3079 Unknown 6 0.0% 5454 909 909
22658 Ephemeral 6 0.0% 6269 1045 1813
30090 Addressable 5 0.0% 7575 1515 1515
34236 Addressable 5 0.0% 61577 12315 21112
22120 Ephemeral 5 0.0% 5493 1099 1133
22119 Ephemeral 5 0.0% 4227 845 846
23194 NWC request 5 0.0% 2554 511 534
1984 Report 5 0.0% 2714 543 662
31000 Addressable 5 0.0% 3024 605 616
1078 Unknown 5 0.0% 70534 14107 14226
10003 Bookmarks 5 0.0% 15698 3140 4954
1000 Unknown 4 0.0% 6474 1618 1782
30087 Addressable 4 0.0% 29300 7325 7325
38111 Addressable 4 0.0% 2996 749 752
25055 Ephemeral 4 0.0% 6286 1572 1788
30421 Addressable 4 0.0% 4079 1020 1028
30079 Addressable 4 0.0% 3500 875 875
30815 Addressable 4 0.0% 150136 37534 37534
13302 Unknown 4 0.0% 30097 7524 22303
22865 Ephemeral 4 0.0% 2785 696 699
10012 Unknown 3 0.0% 1388 463 622
39015 Addressable 3 0.0% 2088 696 696
30787 Addressable 3 0.0% 4118 1373 1374
10001 Pinned notes 3 0.0% 1758 586 586
15129 Unknown 3 0.0% 1365 455 455
37779 Addressable 3 0.0% 1569 523 523
8 Unknown 3 0.0% 1527 509 509
30003 Addressable 3 0.0% 1167 389 389
31512 Addressable 3 0.0% 10234 3411 3414
39050 Addressable 3 0.0% 5529 1843 1843
31237 Addressable 3 0.0% 2439 813 821
30554 Addressable 3 0.0% 1827 609 609
22778 Ephemeral 3 0.0% 4059 1353 1847
30402 Addressable 3 0.0% 2036 679 680
10063 Unknown 3 0.0% 1639 546 627
1631 Unknown 2 0.0% 2620 1310 1310
1315 Unknown 2 0.0% 990 495 495
22760 Ephemeral 2 0.0% 3624 1812 1814
30495 Addressable 2 0.0% 4074 2037 2037
10051 Unknown 2 0.0% 878 439 439
13196 Unknown 2 0.0% 1312 656 656
38421 Addressable 2 0.0% 2140 1070 1070
23333 Ephemeral 2 0.0% 1636 818 818
2333 Unknown 2 0.0% 1812 906 906
24913 Ephemeral 2 0.0% 1164 582 582
22882 Ephemeral 2 0.0% 2000 1000 1164
22617 Ephemeral 2 0.0% 852 426 426
22729 Ephemeral 2 0.0% 1860 930 930
22733 Ephemeral 2 0.0% 3330 1665 1848
23013 Ephemeral 2 0.0% 2965 1482 1485
9 Unknown 2 0.0% 4085 2042 2157
31986 Addressable 2 0.0% 1182 591 591
1063 File metadata 1 0.0% 909 909 909
1060 Unknown 1 0.0% 1207 1207 1207
10312 Unknown 1 0.0% 472 472 472
37368 Addressable 1 0.0% 1236 1236 1236
15750 Unknown 1 0.0% 626 626 626
22598 Ephemeral 1 0.0% 1666 1666 1666
17375 Unknown 1 0.0% 606 606 606
22496 Ephemeral 1 0.0% 1518 1518 1518
20200 Ephemeral 1 0.0% 491 491 491
39019 Addressable 1 0.0% 961 961 961
34238 Addressable 1 0.0% 920 920 920
22732 Ephemeral 1 0.0% 1334 1334 1334
20201 Ephemeral 1 0.0% 491 491 491
12222 Unknown 1 0.0% 579 579 579
31337 Addressable 1 0.0% 818 818 818
23071 Ephemeral 1 0.0% 1910 1910 1910
33333 Addressable 1 0.0% 2346 2346 2346
20 Picture 1 0.0% 445 445 445
1808 Unknown 1 0.0% 683 683 683
30113 Addressable 1 0.0% 770 770 770
34235 Addressable 1 0.0% 1501 1501 1501
10377 Unknown 1 0.0% 664 664 664
11111 Unknown 1 0.0% 382 382 382
10133 Unknown 1 0.0% 499 499 499
30081 Addressable 1 0.0% 1370 1370 1370
10009 Unknown 1 0.0% 422 422 422
30005 Addressable 1 0.0% 9671 9671 9671
10443 Unknown 1 0.0% 676 676 676
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 11:21:56 2026
# Config: cache_all_test_config.jsonc
# Duration: 86400 seconds
[2026-08-02 11:22:33] [RELAY] wss://laantungir.net STATUS: ?
[2026-08-02 11:22:33] [RELAY] wss://nos.lol STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://nostr-pub.wellorder.net STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://nostr.mom STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://nostr.oxtr.dev STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://offchain.pub STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://premium.primal.net STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.damus.io STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.ditto.pub STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.divine.video STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.momostr.pink STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.mostr.pub STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.nostrplebs.com STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://relay.primal.net STATUS: connected
[2026-08-02 11:22:33] [RELAY] wss://theforest.nostr1.com STATUS: connected
[2026-08-02 11:23:06] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:23:06] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:23:07] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:23:08] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:23:08] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:23:09] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:23:38] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:23:38] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:23:45] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:23:45] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:24:08] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:24:08] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:26:10] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:10] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:14] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:15] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:15] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:26:15] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:26:28] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:29] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:38] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:26:38] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:26:43] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:43] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:44] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:45] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:47] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:48] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:49] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:50] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:26:53] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:26:53] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:27:17] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:27:17] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:27:31] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:27:32] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 11:27:58] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:27:58] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:28:10] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:11] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:12] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:12] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:15] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:28:15] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:28:15] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:16] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:17] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:18] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:20] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:20] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:22] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:22] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:24] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:25] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:44] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:45] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:45] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:28:45] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:28:46] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:47] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:51] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:51] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:28:56] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:28:56] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:29:28] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:29:28] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:29:59] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:00] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:01] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:01] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:02] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:03] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:03] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:04] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:06] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:07] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:07] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:08] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:10] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:11] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:14] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:15] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:17] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:18] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:18] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:19] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:19] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:30:19] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:30:20] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:20] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:22] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:23] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:25] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:30:26] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 11:30:58] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:30:58] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:31:19] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:31:19] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:31:28] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:31:28] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:32:19] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:32:19] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:32:28] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:32:28] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:32:46] [RELAY] wss://relay.damus.io DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:32:52] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 11:33:03] [RELAY] wss://relay.damus.io DISCONNECTED: Failed to create WebSocket client for relay wss://relay.damus.io
[2026-08-02 11:33:13] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 11:33:19] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:33:19] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:33:54] [RELAY] wss://relay.damus.io DISCONNECTED: Failed to create WebSocket client for relay wss://relay.damus.io
[2026-08-02 11:33:54] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 11:34:12] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 11:34:12] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:34:24] [RELAY] wss://relay.damus.io DISCONNECTED:
[2026-08-02 11:34:24] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 11:35:13] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 11:35:13] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 11:35:24] [RELAY] wss://relay.damus.io DISCONNECTED:
[2026-08-02 11:35:24] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 11:36:18] [RELAY] wss://relay.damus.io DISCONNECTED: Failed to create WebSocket client for relay wss://relay.damus.io
[2026-08-02 11:36:19] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:36:19] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:36:28] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 11:36:49] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 11:36:49] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 11:37:14] [RELAY] wss://relay.nostrplebs.com DISCONNECTED:
[2026-08-02 11:37:14] [RELAY] wss://relay.nostrplebs.com CONNECTED
[2026-08-02 11:37:19] [RELAY] wss://premium.primal.net DISCONNECTED:
[2026-08-02 11:37:19] [RELAY] wss://premium.primal.net CONNECTED
@@ -0,0 +1,646 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 58m 5s
=== Global Summary ===
Total events: 140345
Total bytes: 182488517 (174.03 MB)
Avg event size: 1300 bytes
Max event: 62825 bytes (kind 1)
Unique pubkeys: 26918
Relays: 15
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://laantungir.net 0 0 0 0 0 0 DOWN
wss://nos.lol 25038 0 0 0 0 54 OK
wss://nostr-pub.wellorder.net 3608 0 0 0 0 0 OK
wss://nostr.mom 8692 0 0 0 0 0 OK
wss://nostr.oxtr.dev 4360 0 0 0 0 7 OK
wss://offchain.pub 17857 0 0 0 0 0 OK
wss://premium.primal.net 171 0 0 0 0 26 OK
wss://relay.damus.io 21539 0 0 0 0 0 OK
wss://relay.ditto.pub 22251 0 0 0 0 7 OK
wss://relay.divine.video 281 0 0 0 0 0 OK
wss://relay.momostr.pink 1812 0 0 0 0 7 OK
wss://relay.mostr.pub 20917 0 0 0 0 10 OK
wss://relay.nostrplebs.com 23 0 0 0 0 0 OK
wss://relay.primal.net 12945 0 0 0 0 57 OK
wss://theforest.nostr1.com 851 0 0 0 0 0 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
21059 Gift wrap (NIP-59) 22589 16.1% 76886982 3404 22354
30382 Addressable 15147 10.8% 6854204 453 484
5 Deletion 10634 7.6% 4652532 438 955
20001 Ephemeral 4553 3.2% 2990229 657 26578
22591 Ephemeral 4464 3.2% 3384073 758 1230
1 Text note 4307 3.1% 4175360 969 62825
22668 Ephemeral 4214 3.0% 1807801 429 429
22466 Ephemeral 3541 2.5% 1519089 429 429
30383 Addressable 3125 2.2% 1507650 482 550
0 Profile metadata 2878 2.1% 2533064 880 9187
22551 Ephemeral 2857 2.0% 1233111 432 1160
30078 App data 2566 1.8% 8335901 3249 55090
38501 Addressable 2349 1.7% 1740867 741 744
22912 Ephemeral 1869 1.3% 802701 429 3197
22684 Ephemeral 1859 1.3% 803991 432 3193
22734 Ephemeral 1629 1.2% 695583 427 427
10002 Relay list 1604 1.1% 4950855 3087 42385
7 Reaction 1435 1.0% 847949 591 1287
29333 Ephemeral 1408 1.0% 690797 491 492
24133 Ephemeral 1375 1.0% 1890802 1375 38738
37195 Addressable 1338 1.0% 1004351 751 934
25555 Ephemeral 1181 0.8% 5929927 5021 17075
22690 Ephemeral 1081 0.8% 467584 433 3184
22601 Ephemeral 990 0.7% 423742 428 429
22850 Ephemeral 947 0.7% 407208 430 430
4515 Unknown 868 0.6% 1923082 2216 55180
23334 Ephemeral 859 0.6% 374353 436 439
22655 Ephemeral 821 0.6% 351388 428 428
6 Repost 811 0.6% 771135 951 10128
22795 Ephemeral 778 0.6% 1060284 1363 3201
4 Encrypted DM 767 0.5% 725743 946 19618
22720 Ephemeral 749 0.5% 1079163 1441 3249
22876 Ephemeral 724 0.5% 308478 426 431
22676 Ephemeral 696 0.5% 297193 427 428
22913 Ephemeral 678 0.5% 294988 435 3201
22689 Ephemeral 663 0.5% 1094513 1651 5926
22927 Ephemeral 607 0.4% 1017238 1676 1876
22236 Ephemeral 595 0.4% 446548 751 814
22911 Ephemeral 569 0.4% 951997 1673 1880
22943 Ephemeral 563 0.4% 249401 443 1658
22603 Ephemeral 543 0.4% 231317 426 427
22743 Ephemeral 542 0.4% 231978 428 430
22695 Ephemeral 538 0.4% 871272 1619 1850
1059 Gift wrap seal 535 0.4% 2557163 4780 55121
22842 Ephemeral 528 0.4% 882417 1671 1853
24242 Ephemeral 527 0.4% 305133 579 579
13194 NWC info 505 0.4% 259258 513 514
22730 Ephemeral 474 0.3% 418919 884 1517
11998 Unknown 449 0.3% 208257 464 465
23337 Ephemeral 446 0.3% 252660 567 586
7368 Unknown 443 0.3% 459822 1038 1059
22816 Ephemeral 410 0.3% 183597 448 3196
30315 User status 406 0.3% 280885 692 1166
22456 Ephemeral 403 0.3% 196962 489 790
22815 Ephemeral 386 0.3% 620350 1607 3199
20387 Ephemeral 368 0.3% 189152 514 514
20000 Ephemeral 351 0.3% 237048 675 17065
5300 Unknown 350 0.2% 213715 611 701
22774 Ephemeral 349 0.2% 149372 428 428
31990 App handler info 343 0.2% 389821 1137 6861
3 Follow list 325 0.2% 4492770 13824 58982
23003 Ephemeral 317 0.2% 135676 428 428
38502 Addressable 295 0.2% 113575 385 385
1985 Unknown 287 0.2% 189598 661 4834
22807 Ephemeral 279 0.2% 119596 429 430
25050 Ephemeral 272 0.2% 113448 417 1362
4004 Unknown 270 0.2% 315630 1169 1169
20004 Ephemeral 233 0.2% 124155 533 549
35000 Addressable 208 0.1% 160992 774 774
22613 Ephemeral 208 0.1% 143395 689 1134
22592 Ephemeral 201 0.1% 93351 464 3193
20333 Ephemeral 197 0.1% 156942 797 798
31950 Addressable 196 0.1% 170818 872 937
22632 Ephemeral 195 0.1% 85266 437 3204
9735 Zap receipt 193 0.1% 370520 1920 4167
10100 Unknown 188 0.1% 279911 1489 3826
30311 Addressable 187 0.1% 202343 1082 1197
24913 Ephemeral 179 0.1% 79157 442 582
23004 Ephemeral 179 0.1% 77149 431 431
22854 Ephemeral 174 0.1% 137235 789 1660
30384 Addressable 166 0.1% 85437 515 556
22891 Ephemeral 164 0.1% 235679 1437 3243
10050 DM relays 160 0.1% 72634 454 592
30023 Long-form article 144 0.1% 954406 6628 13594
6300 Unknown 144 0.1% 880738 6116 17462
445 Encrypted group msg 140 0.1% 350204 2501 4134
10000 Mute list 136 0.1% 274284 2017 27742
22693 Ephemeral 130 0.1% 101515 781 2619
30166 Addressable 128 0.1% 66754 522 808
4244 Unknown 127 0.1% 71628 564 564
38503 Addressable 125 0.1% 68916 551 552
20787 Ephemeral 124 0.1% 181428 1463 2478
22455 Ephemeral 112 0.1% 183137 1635 1847
22616 Ephemeral 111 0.1% 98460 887 3453
22543 Ephemeral 107 0.1% 172800 1615 1849
22885 Ephemeral 106 0.1% 102539 967 3524
22672 Ephemeral 104 0.1% 174090 1674 1849
22717 Ephemeral 99 0.1% 120874 1221 1849
22929 Ephemeral 90 0.1% 88203 980 1517
10777 Unknown 89 0.1% 43507 489 489
22829 Ephemeral 89 0.1% 43243 486 3200
30088 Addressable 87 0.1% 162094 1863 1871
30091 Addressable 85 0.1% 52311 615 629
21584 Ephemeral 83 0.1% 36413 439 439
38385 Addressable 82 0.1% 111350 1358 1496
22864 Ephemeral 82 0.1% 56300 687 1223
39101 Addressable 81 0.1% 43173 533 533
22751 Ephemeral 81 0.1% 73608 909 4083
1111 Comment 79 0.1% 93490 1183 2457
3927 Unknown 78 0.1% 41585 533 544
10011 Unknown 77 0.1% 33123 430 431
21060 Ephemeral 74 0.1% 42180 570 570
21617 Ephemeral 73 0.1% 36119 495 495
33334 Addressable 70 0.0% 58170 831 831
30380 Addressable 66 0.0% 29964 454 454
38383 Addressable 66 0.0% 59966 909 3211
38384 Addressable 62 0.0% 46066 743 743
38500 Addressable 60 0.0% 42716 712 713
20384 Ephemeral 59 0.0% 37701 639 639
30089 Chunked data 53 0.0% 614992 11604 44372
1002 Unknown 53 0.0% 48203 909 1011
22840 Ephemeral 52 0.0% 22285 429 430
23066 Ephemeral 50 0.0% 43751 875 1165
22526 Ephemeral 50 0.0% 21469 429 430
22405 Ephemeral 47 0.0% 20886 444 445
23042 Ephemeral 44 0.0% 18876 429 429
22514 Ephemeral 44 0.0% 18832 428 428
22748 Ephemeral 44 0.0% 18874 429 429
31510 Addressable 43 0.0% 42075 978 982
3079 Unknown 42 0.0% 38178 909 909
4243 Unknown 42 0.0% 23688 564 564
22862 Ephemeral 41 0.0% 17578 429 429
22475 Ephemeral 40 0.0% 43786 1095 3923
22721 Ephemeral 40 0.0% 23480 587 1842
22674 Ephemeral 39 0.0% 17992 461 1661
4245 Unknown 39 0.0% 17316 444 444
30080 Addressable 39 0.0% 37674 966 1011
30443 Addressable 38 0.0% 46231 1217 1547
30515 Addressable 38 0.0% 201552 5304 5304
22212 Ephemeral 37 0.0% 16440 444 445
10003 Bookmarks 37 0.0% 420277 11359 56461
38225 Addressable 35 0.0% 33592 960 1021
30815 Addressable 34 0.0% 1276156 37534 37534
1984 Report 34 0.0% 22174 652 667
22753 Ephemeral 33 0.0% 40204 1218 3766
30618 Addressable 32 0.0% 45089 1409 8804
1040 Unknown 32 0.0% 36596 1144 5164
3434 Unknown 30 0.0% 20382 679 705
7000 Unknown 28 0.0% 21752 777 822
30385 Addressable 28 0.0% 22344 798 981
33301 Addressable 27 0.0% 45586 1688 2162
16 Generic repost 27 0.0% 21778 807 1604
22119 Ephemeral 25 0.0% 21137 845 846
22120 Ephemeral 25 0.0% 27207 1088 1128
24421 Ephemeral 24 0.0% 12384 516 516
22525 Ephemeral 24 0.0% 42802 1783 4259
22756 Ephemeral 24 0.0% 32806 1367 3198
31124 Addressable 23 0.0% 28396 1235 1445
22733 Ephemeral 23 0.0% 15376 669 1224
30617 Addressable 23 0.0% 25047 1089 1089
22649 Ephemeral 22 0.0% 9459 430 430
22680 Ephemeral 21 0.0% 14310 681 3193
22874 Ephemeral 21 0.0% 24675 1175 1850
31339 Addressable 21 0.0% 30870 1470 1470
30000 Addressable 19 0.0% 16519 869 3185
22688 Ephemeral 19 0.0% 18248 960 3195
31991 Addressable 19 0.0% 8759 461 461
39842 Addressable 18 0.0% 19919 1107 1385
10312 Unknown 18 0.0% 9219 512 559
7403 Unknown 17 0.0% 8336 490 491
30280 Addressable 17 0.0% 8721 513 513
14 Unknown 16 0.0% 17438 1090 1531
31000 Addressable 16 0.0% 9716 607 616
22645 Ephemeral 16 0.0% 9285 580 1838
2333 Unknown 15 0.0% 10074 672 722
34236 Addressable 15 0.0% 211139 14076 26491
30079 Addressable 15 0.0% 47271 3151 4574
23333 Ephemeral 15 0.0% 8754 584 634
23194 NWC request 15 0.0% 8952 597 1086
22778 Ephemeral 15 0.0% 6421 428 429
38111 Addressable 14 0.0% 10495 750 752
22747 Ephemeral 13 0.0% 8895 684 1657
25000 Ephemeral 13 0.0% 23498 1808 2214
22506 Ephemeral 13 0.0% 23855 1835 1841
1000 Unknown 13 0.0% 21880 1683 1790
26428 Ephemeral 12 0.0% 10516 876 877
23195 NWC response 12 0.0% 9184 765 1183
1078 Unknown 12 0.0% 169653 14138 14241
30554 Addressable 12 0.0% 7292 608 608
22361 Ephemeral 11 0.0% 4708 428 428
30421 Addressable 11 0.0% 11427 1039 1066
30085 Addressable 11 0.0% 7227 657 659
10063 Unknown 11 0.0% 5982 544 627
22402 Ephemeral 11 0.0% 4683 426 443
39015 Addressable 11 0.0% 7656 696 696
11125 Unknown 10 0.0% 10262 1026 1916
30113 Addressable 10 0.0% 6987 699 770
33051 Addressable 10 0.0% 5206 521 522
30087 Addressable 9 0.0% 65925 7325 7325
30213 Addressable 9 0.0% 5662 629 637
1018 Unknown 8 0.0% 4500 562 677
30090 Addressable 8 0.0% 12120 1515 1515
1050 Unknown 8 0.0% 3496 437 437
22619 Ephemeral 8 0.0% 11165 1396 1842
10051 Unknown 7 0.0% 3205 458 489
26429 Ephemeral 7 0.0% 3437 491 491
38421 Addressable 7 0.0% 7485 1069 1070
39019 Addressable 6 0.0% 5766 961 961
9842 Unknown 6 0.0% 6742 1124 1260
20200 Ephemeral 6 0.0% 2946 491 491
25742 Ephemeral 6 0.0% 11756 1959 7334
30081 Addressable 6 0.0% 8220 1370 1370
20 Picture 6 0.0% 4558 760 852
20201 Ephemeral 6 0.0% 2946 491 491
22722 Ephemeral 5 0.0% 7756 1551 1660
7336 Unknown 5 0.0% 3280 656 656
22825 Ephemeral 5 0.0% 5660 1132 1848
13196 Unknown 5 0.0% 3280 656 656
9841 Unknown 5 0.0% 14029 2806 4468
23336 Ephemeral 5 0.0% 3285 657 657
1063 File metadata 5 0.0% 4593 919 941
31344 Addressable 4 0.0% 2500 625 625
31986 Addressable 4 0.0% 2372 593 593
22432 Ephemeral 4 0.0% 5412 1353 1843
31985 Addressable 4 0.0% 2534 634 667
10015 Unknown 3 0.0% 11266 3755 4578
1010 Unknown 3 0.0% 2505 835 1152
37779 Addressable 3 0.0% 1569 523 523
21734 Ephemeral 3 0.0% 1131 377 377
22671 Ephemeral 3 0.0% 1281 427 427
31343 Addressable 2 0.0% 53115 26558 45456
16767 Unknown 2 0.0% 1974 987 987
1058 Unknown 2 0.0% 1186 593 593
22724 Ephemeral 2 0.0% 2103 1052 1674
30005 Addressable 2 0.0% 11351 5676 9817
21724 Ephemeral 2 0.0% 754 377 377
20177 Ephemeral 2 0.0% 851 426 426
2003 Unknown 2 0.0% 8711 4356 7015
30003 Addressable 2 0.0% 778 389 389
25582 Ephemeral 2 0.0% 4500 2250 3690
38422 Addressable 2 0.0% 60198 30099 30212
22387 Ephemeral 2 0.0% 887 444 444
1618 Unknown 1 0.0% 2168 2168 2168
10001 Pinned notes 1 0.0% 710 710 710
30758 Addressable 1 0.0% 436 436 436
32267 Addressable 1 0.0% 1693 1693 1693
1069 Unknown 1 0.0% 757 757 757
31345 Addressable 1 0.0% 8666 8666 8666
21840 Ephemeral 1 0.0% 377 377 377
21764 Ephemeral 1 0.0% 377 377 377
31353 Addressable 1 0.0% 737 737 737
1808 Unknown 1 0.0% 1696 1696 1696
31633 Addressable 1 0.0% 592 592 592
30066 Addressable 1 0.0% 675 675 675
30063 Addressable 1 0.0% 2891 2891 2891
21711 Ephemeral 1 0.0% 377 377 377
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 15:24:51 2026
# Config: cache_all_test_config.jsonc
# Duration: 86400 seconds
[2026-08-02 15:25:27] [RELAY] wss://laantungir.net STATUS: ?
[2026-08-02 15:25:27] [RELAY] wss://nos.lol STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://nostr-pub.wellorder.net STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://nostr.mom STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://nostr.oxtr.dev STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://offchain.pub STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://premium.primal.net STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://relay.damus.io STATUS: ?
[2026-08-02 15:25:27] [RELAY] wss://relay.ditto.pub STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://relay.divine.video STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://relay.momostr.pink STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://relay.mostr.pub STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://relay.nostrplebs.com STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://relay.primal.net STATUS: connected
[2026-08-02 15:25:27] [RELAY] wss://theforest.nostr1.com STATUS: connected
[2026-08-02 15:25:39] [RELAY] wss://relay.damus.io CONNECTED
[2026-08-02 15:26:24] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:26:24] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 15:26:46] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:26:46] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:27:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:27:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:27:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:27:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:28:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:28:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:28:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:28:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:28:46] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:28:46] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:29:16] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:29:16] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:29:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:29:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:30:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:30:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:30:16] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:30:16] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:31:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:31:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:31:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:31:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:32:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:32:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:32:37] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:32:38] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:33:19] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:33:19] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:33:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:33:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:33:49] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:33:49] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:34:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:34:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:34:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:34:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:35:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:35:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:35:11] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:35:11] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:35:12] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:35:13] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 15:35:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:35:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:35:41] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:35:41] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:35:54] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:35:55] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:37:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:37:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:37:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:37:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:38:11] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:38:11] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:38:34] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:38:35] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 15:39:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:39:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:39:17] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:39:18] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:39:32] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:39:32] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 15:39:34] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:39:34] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 15:39:59] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:39:59] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:40:07] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:40:07] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 15:40:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:40:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:40:40] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:40:41] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 15:41:29] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:41:29] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:41:39] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:41:40] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:41:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:41:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:42:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:42:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:42:51] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:42:51] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:44:41] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:44:41] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:45:00] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:45:01] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 15:45:07] [RELAY] wss://relay.ditto.pub DISCONNECTED:
[2026-08-02 15:45:07] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:45:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:45:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:45:26] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:45:27] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:45:37] [RELAY] wss://relay.ditto.pub DISCONNECTED:
[2026-08-02 15:45:37] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:46:07] [RELAY] wss://relay.ditto.pub DISCONNECTED:
[2026-08-02 15:46:07] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:46:08] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:46:08] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:46:10] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:46:10] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:46:37] [RELAY] wss://relay.ditto.pub DISCONNECTED:
[2026-08-02 15:46:37] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:46:38] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:46:38] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:46:40] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:46:40] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:46:51] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:46:52] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:47:00] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:01] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:47:01] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:02] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:47:07] [RELAY] wss://relay.ditto.pub DISCONNECTED:
[2026-08-02 15:47:07] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:47:07] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:08] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:47:08] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:09] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:47:17] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:18] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:47:18] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:19] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:47:33] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:47:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:48:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:48:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:48:41] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:48:42] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 15:49:03] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:49:03] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:49:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:49:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:49:37] [RELAY] wss://relay.ditto.pub DISCONNECTED:
[2026-08-02 15:49:37] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:50:02] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:50:02] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:50:05] [RELAY] wss://relay.ditto.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:50:05] [RELAY] wss://relay.ditto.pub CONNECTED
[2026-08-02 15:50:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:50:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:51:18] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:51:19] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:51:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:51:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:52:00] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:52:00] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:52:30] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:52:30] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:52:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:52:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:52:33] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:52:34] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:52:34] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:52:35] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:53:00] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:53:00] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:53:30] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 15:53:30] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:53:30] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:53:30] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:53:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:53:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:54:19] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:54:19] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:54:54] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:54:55] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:54:56] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:54:56] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:54:57] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:54:58] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:54:58] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:54:59] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:01] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:02] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:02] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:04] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:04] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:55:04] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:55:04] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:05] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:06] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:06] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:07] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:07] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:08] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:09] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:55:09] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:09] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:55:11] [RELAY] wss://nostr.oxtr.dev DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:12] [RELAY] wss://nostr.oxtr.dev CONNECTED
[2026-08-02 15:55:33] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 15:55:33] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:55:36] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:37] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 15:55:50] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:55:50] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:56:09] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:56:10] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 15:56:12] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:56:13] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 15:57:22] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:57:23] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 15:58:04] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 15:58:04] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:00:01] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:00:01] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:00:31] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:00:31] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:00:54] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:00:54] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 16:01:31] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:01:31] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:01:48] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:01:48] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:01:55] [RELAY] wss://relay.mostr.pub DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:01:55] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 16:02:29] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:02:29] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:02:59] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:02:59] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:03:01] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:03:01] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:03:29] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:03:29] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:03:31] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:03:31] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:03:59] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:03:59] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:04:29] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:04:29] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:04:59] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:04:59] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:05:10] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:11] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:12] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:13] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:15] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:16] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:17] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:18] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:19] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:20] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:21] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:21] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:24] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:05:25] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:05:59] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:05:59] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:06:09] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:06:09] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:08:00] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:08:01] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:08:09] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:08:09] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:08:42] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:08:42] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:10:20] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:10:21] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:11:09] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:11:09] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:11:26] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:11:27] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:11:30] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:11:30] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:11:32] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:11:32] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:11:39] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:11:39] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:11:46] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:11:47] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:12:58] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:12:58] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:13:25] [RELAY] wss://relay.mostr.pub DISCONNECTED:
[2026-08-02 16:13:25] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 16:13:32] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:13:32] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:13:58] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:13:58] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:14:28] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:14:28] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:14:32] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:14:32] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:15:16] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:15:17] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:15:23] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:15:24] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:15:32] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:15:32] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:15:41] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:15:42] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:16:25] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:16:26] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:16:28] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:16:28] [RELAY] wss://premium.primal.net CONNECTED
[2026-08-02 16:16:32] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:16:32] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:16:36] [RELAY] wss://relay.momostr.pink DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:16:36] [RELAY] wss://relay.momostr.pink CONNECTED
[2026-08-02 16:17:02] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:17:02] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:17:25] [RELAY] wss://relay.mostr.pub DISCONNECTED:
[2026-08-02 16:17:25] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 16:18:02] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:18:02] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:18:08] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:18:08] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:18:25] [RELAY] wss://relay.mostr.pub DISCONNECTED:
[2026-08-02 16:18:25] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 16:18:49] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:18:49] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:18:55] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:18:56] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:19:37] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:19:37] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:19:40] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:19:40] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:20:37] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:20:37] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:21:07] [RELAY] wss://relay.primal.net DISCONNECTED:
[2026-08-02 16:21:07] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:21:10] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:21:10] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:21:14] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:21:15] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:21:56] [RELAY] wss://nos.lol DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:21:56] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:22:26] [RELAY] wss://nos.lol DISCONNECTED:
[2026-08-02 16:22:26] [RELAY] wss://nos.lol CONNECTED
[2026-08-02 16:22:55] [RELAY] wss://relay.mostr.pub DISCONNECTED:
[2026-08-02 16:22:55] [RELAY] wss://relay.mostr.pub CONNECTED
[2026-08-02 16:23:32] [RELAY] wss://premium.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:23:33] [RELAY] wss://premium.primal.net CONNECTED
@@ -0,0 +1,243 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 8m 23s
=== Global Summary ===
Total events: 7884
Total bytes: 12205231 (11.64 MB)
Avg event size: 1548 bytes
Max event: 56750 bytes (kind 3)
Unique pubkeys: 2301
Relays: 1
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 7884 0 0 0 0 10 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
1059 Gift wrap seal 871 11.0% 1657832 1903 55121
21059 Gift wrap (NIP-59) 740 9.4% 2308644 3120 22354
30078 App data 622 7.9% 2651879 4263 55090
30383 Addressable 520 6.6% 278610 536 548
20001 Ephemeral 386 4.9% 403111 1044 2026
22734 Ephemeral 357 4.5% 152451 427 428
1 Text note 303 3.8% 303990 1003 4648
30382 Addressable 288 3.7% 137207 476 484
0 Profile metadata 189 2.4% 140594 744 2012
29333 Ephemeral 165 2.1% 81180 492 492
10002 Relay list 158 2.0% 571188 3615 42385
22236 Ephemeral 158 2.0% 120802 765 801
5 Deletion 154 2.0% 73847 480 664
4515 Unknown 154 2.0% 463562 3010 38797
22743 Ephemeral 134 1.7% 57352 428 428
7 Reaction 113 1.4% 62452 553 727
24133 Ephemeral 102 1.3% 132440 1298 7334
22684 Ephemeral 99 1.3% 42669 431 431
22850 Ephemeral 94 1.2% 40420 430 430
22838 Ephemeral 88 1.1% 37752 429 429
22774 Ephemeral 84 1.1% 35952 428 428
23003 Ephemeral 84 1.1% 35952 428 428
22876 Ephemeral 83 1.1% 35358 426 426
22611 Ephemeral 75 1.0% 32175 429 429
25555 Ephemeral 69 0.9% 281319 4077 16987
4 Encrypted DM 66 0.8% 72028 1091 17018
22613 Ephemeral 63 0.8% 43489 690 692
25050 Ephemeral 61 0.8% 24461 401 401
11998 Unknown 61 0.8% 28269 463 465
443 Unknown 49 0.6% 68056 1389 22480
30301 Addressable 47 0.6% 53284 1134 1912
3927 Unknown 47 0.6% 25051 533 533
20387 Ephemeral 44 0.6% 22616 514 514
22761 Ephemeral 44 0.6% 18832 428 428
13194 NWC info 43 0.5% 22043 513 514
20004 Ephemeral 42 0.5% 22266 530 549
5300 Unknown 42 0.5% 25437 606 637
22546 Ephemeral 39 0.5% 16614 426 426
22714 Ephemeral 37 0.5% 15836 428 428
30166 Addressable 35 0.4% 17904 512 589
9735 Zap receipt 35 0.4% 70344 2010 3538
23334 Ephemeral 35 0.4% 15084 431 432
1985 Unknown 34 0.4% 16232 477 483
22816 Ephemeral 33 0.4% 14157 429 429
21050 Ephemeral 33 0.4% 30862 935 1874
30315 User status 32 0.4% 21166 661 925
445 Encrypted group msg 32 0.4% 120956 3780 9346
22698 Ephemeral 32 0.4% 13664 427 427
4004 Unknown 32 0.4% 37408 1169 1169
38501 Addressable 31 0.4% 23034 743 744
31990 App handler info 25 0.3% 9675 387 387
10100 Unknown 24 0.3% 27687 1154 1589
20333 Ephemeral 24 0.3% 19120 797 798
3 Follow list 23 0.3% 328284 14273 56750
24913 Ephemeral 22 0.3% 9700 441 493
30311 Addressable 21 0.3% 22061 1051 1197
6 Repost 21 0.3% 49793 2371 12925
10050 DM relays 19 0.2% 8172 430 450
22712 Ephemeral 18 0.2% 7704 428 428
22808 Ephemeral 17 0.2% 7293 429 429
10000 Mute list 16 0.2% 26659 1666 12630
4244 Unknown 15 0.2% 8460 564 564
23025 Ephemeral 15 0.2% 6465 431 431
31234 Addressable 14 0.2% 24051 1718 2268
38503 Addressable 13 0.2% 7174 552 552
22630 Ephemeral 12 0.2% 11762 980 1654
37195 Addressable 12 0.2% 8752 729 934
6300 Unknown 12 0.2% 77247 6437 17248
10777 Unknown 12 0.2% 5861 488 489
30091 Addressable 11 0.1% 6763 615 629
20000 Ephemeral 11 0.1% 3806 346 346
30515 Addressable 11 0.1% 58344 5304 5304
30088 Addressable 11 0.1% 20483 1862 1871
22361 Ephemeral 10 0.1% 4439 444 445
30280 Addressable 9 0.1% 4617 513 513
22902 Ephemeral 9 0.1% 3861 429 429
22506 Ephemeral 8 0.1% 3416 427 427
7368 Unknown 8 0.1% 8304 1038 1040
22645 Ephemeral 8 0.1% 3432 429 429
38500 Addressable 8 0.1% 5696 712 713
33334 Addressable 8 0.1% 6648 831 831
30380 Addressable 8 0.1% 3632 454 454
22688 Ephemeral 7 0.1% 2989 427 427
22656 Ephemeral 7 0.1% 3017 431 431
22316 Ephemeral 7 0.1% 3106 444 444
30089 Chunked data 6 0.1% 123676 20613 44372
22456 Ephemeral 6 0.1% 3140 523 790
22739 Ephemeral 6 0.1% 2580 430 430
22882 Ephemeral 6 0.1% 5292 882 1449
22862 Ephemeral 6 0.1% 2574 429 429
1111 Comment 6 0.1% 7227 1204 1698
4245 Unknown 5 0.1% 2220 444 444
30512 Addressable 5 0.1% 7504 1501 1506
22641 Ephemeral 5 0.1% 2145 429 429
22586 Ephemeral 5 0.1% 2135 427 427
4243 Unknown 5 0.1% 2820 564 564
22835 Ephemeral 5 0.1% 2145 429 429
22620 Ephemeral 5 0.1% 2145 429 429
22298 Ephemeral 5 0.1% 2218 444 444
22852 Ephemeral 5 0.1% 2140 428 428
22726 Ephemeral 5 0.1% 2150 430 430
22362 Ephemeral 5 0.1% 2218 444 444
22772 Ephemeral 5 0.1% 4051 810 1132
22805 Ephemeral 4 0.1% 1712 428 428
9734 Zap request 4 0.1% 3756 939 1033
22200 Ephemeral 4 0.1% 1774 444 444
31510 Addressable 4 0.1% 3922 980 982
22570 Ephemeral 4 0.1% 1716 429 429
25195 Ephemeral 4 0.1% 2712 678 678
22752 Ephemeral 3 0.0% 4047 1349 3191
31339 Addressable 3 0.0% 4410 1470 1470
22949 Ephemeral 3 0.0% 1287 429 429
22358 Ephemeral 3 0.0% 1331 444 444
22474 Ephemeral 3 0.0% 1330 443 444
22185 Ephemeral 3 0.0% 1330 443 444
22936 Ephemeral 3 0.0% 1290 430 430
25196 Ephemeral 3 0.0% 4938 1646 2214
22439 Ephemeral 3 0.0% 1330 443 444
22824 Ephemeral 3 0.0% 1281 427 427
22270 Ephemeral 3 0.0% 1330 443 444
22263 Ephemeral 3 0.0% 1330 443 444
22755 Ephemeral 3 0.0% 1284 428 428
10051 Unknown 3 0.0% 1317 439 439
34775 Addressable 3 0.0% 7599 2533 2561
30300 Addressable 3 0.0% 5502 1834 1834
22234 Ephemeral 3 0.0% 1330 443 444
7403 Unknown 2 0.0% 978 489 489
22626 Ephemeral 2 0.0% 1916 958 1486
30079 Addressable 2 0.0% 1750 875 875
15750 Unknown 2 0.0% 1242 621 626
23337 Ephemeral 2 0.0% 1027 514 514
30815 Addressable 2 0.0% 75068 37534 37534
30213 Addressable 2 0.0% 1264 632 632
22716 Ephemeral 2 0.0% 854 427 427
22492 Ephemeral 2 0.0% 887 444 444
30000 Addressable 2 0.0% 1646 823 823
22463 Ephemeral 2 0.0% 887 444 444
22304 Ephemeral 2 0.0% 887 444 444
10003 Bookmarks 2 0.0% 20016 10008 15003
30443 Addressable 2 0.0% 2572 1286 1312
22233 Ephemeral 2 0.0% 886 443 443
22275 Ephemeral 2 0.0% 887 444 444
30087 Addressable 2 0.0% 11238 5619 7325
20078 Ephemeral 2 0.0% 1458 729 729
22057 Ephemeral 2 0.0% 887 444 444
22251 Ephemeral 2 0.0% 887 444 444
22181 Ephemeral 2 0.0% 887 444 444
22678 Ephemeral 2 0.0% 1388 694 694
22747 Ephemeral 2 0.0% 858 429 429
22410 Ephemeral 2 0.0% 887 444 444
22334 Ephemeral 2 0.0% 887 444 444
22249 Ephemeral 2 0.0% 887 444 444
22258 Ephemeral 2 0.0% 887 444 444
30090 Addressable 2 0.0% 2588 1294 1515
10312 Unknown 1 0.0% 472 472 472
22853 Ephemeral 1 0.0% 1673 1673 1673
22303 Ephemeral 1 0.0% 443 443 443
22306 Ephemeral 1 0.0% 443 443 443
20200 Ephemeral 1 0.0% 491 491 491
22132 Ephemeral 1 0.0% 443 443 443
20201 Ephemeral 1 0.0% 491 491 491
22245 Ephemeral 1 0.0% 443 443 443
22352 Ephemeral 1 0.0% 443 443 443
22240 Ephemeral 1 0.0% 443 443 443
34236 Addressable 1 0.0% 931 931 931
17 Unknown 1 0.0% 610 610 610
22286 Ephemeral 1 0.0% 443 443 443
13196 Unknown 1 0.0% 656 656 656
20242 Ephemeral 1 0.0% 346 346 346
7376 Unknown 1 0.0% 670 670 670
38385 Addressable 1 0.0% 1350 1350 1350
22355 Ephemeral 1 0.0% 443 443 443
23333 Ephemeral 1 0.0% 571 571 571
22737 Ephemeral 1 0.0% 556 556 556
22291 Ephemeral 1 0.0% 443 443 443
30421 Addressable 1 0.0% 995 995 995
22860 Ephemeral 1 0.0% 553 553 553
2333 Unknown 1 0.0% 659 659 659
22219 Ephemeral 1 0.0% 443 443 443
22382 Ephemeral 1 0.0% 443 443 443
23032 Ephemeral 1 0.0% 430 430 430
7375 Unknown 1 0.0% 1822 1822 1822
22579 Ephemeral 1 0.0% 1670 1670 1670
10007 Unknown 1 0.0% 715 715 715
10086 Unknown 1 0.0% 715 715 715
22729 Ephemeral 1 0.0% 3030 3030 3030
22540 Ephemeral 1 0.0% 426 426 426
22671 Ephemeral 1 0.0% 3194 3194 3194
30618 Addressable 1 0.0% 460 460 460
23118 Ephemeral 1 0.0% 430 430 430
38421 Addressable 1 0.0% 1069 1069 1069
10377 Unknown 1 0.0% 664 664 664
11111 Unknown 1 0.0% 382 382 382
34550 Community def 1 0.0% 867 867 867
22307 Ephemeral 1 0.0% 443 443 443
1078 Unknown 1 0.0% 13969 13969 13969
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 16:24:15 2026
# Config: cache_all_test_primal.jsonc
# Duration: 3600 seconds
[2026-08-02 16:24:32] [RELAY] wss://relay.primal.net STATUS: connected
[2026-08-02 16:24:33] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:24:34] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:24:37] [RELAY] ? EOSE
[2026-08-02 16:28:28] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:28:29] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:29:27] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:29:28] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:29:32] [RELAY] ? EOSE
[2026-08-02 16:29:45] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:29:46] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:30:42] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:30:43] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:30:54] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:30:55] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:32:29] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:32:30] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:32:30] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:32:31] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:32:31] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:32:32] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:32:48] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:32:49] [RELAY] wss://relay.primal.net CONNECTED
@@ -0,0 +1,217 @@
=== Cache-All Feasibility Test Report ===
Duration: 0h 6m 16s
=== Global Summary ===
Total events: 5992
Total bytes: 8743262 (8.34 MB)
Avg event size: 1459 bytes
Max event: 61894 bytes (kind 3)
Unique pubkeys: 1142
Relays: 1
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Err Disc Status
wss://relay.primal.net 5992 0 0 0 0 3 OK
=== Kind Distribution ===
Kind Name Count % Total Bytes Avg Max
30078 App data 495 8.3% 2024957 4091 54029
21059 Gift wrap (NIP-59) 386 6.4% 1099128 2847 5314
4515 Unknown 374 6.2% 781718 2090 14221
22591 Ephemeral 371 6.2% 286839 773 1228
20001 Ephemeral 271 4.5% 315643 1165 2029
22734 Ephemeral 264 4.4% 112728 427 427
30382 Addressable 255 4.3% 121375 476 483
30383 Addressable 237 4.0% 126938 536 547
1 Text note 212 3.5% 211399 997 2011
5 Deletion 183 3.1% 91915 502 664
22236 Ephemeral 146 2.4% 104252 714 810
0 Profile metadata 145 2.4% 103418 713 2012
7 Reaction 138 2.3% 79421 576 1036
22743 Ephemeral 134 2.2% 57352 428 428
1059 Gift wrap seal 132 2.2% 430222 3259 5285
29333 Ephemeral 130 2.2% 63960 492 492
22611 Ephemeral 117 2.0% 50147 429 429
10002 Relay list 114 1.9% 510211 4476 42449
23456 Ephemeral 72 1.2% 121616 1689 1874
22850 Ephemeral 71 1.2% 30530 430 430
22684 Ephemeral 70 1.2% 30170 431 431
22838 Ephemeral 63 1.1% 27027 429 429
23003 Ephemeral 63 1.1% 26964 428 428
22774 Ephemeral 62 1.0% 26536 428 428
22876 Ephemeral 61 1.0% 28880 473 3193
22583 Ephemeral 61 1.0% 26047 427 427
22613 Ephemeral 60 1.0% 43172 720 1133
4 Encrypted DM 58 1.0% 61778 1065 17018
25555 Ephemeral 56 0.9% 249772 4460 16895
25050 Ephemeral 51 0.9% 20451 401 401
11998 Unknown 48 0.8% 22272 464 465
22728 Ephemeral 48 0.8% 20592 429 429
31234 Addressable 33 0.6% 77859 2359 2612
13194 NWC info 32 0.5% 16448 514 514
20387 Ephemeral 32 0.5% 16448 514 514
24133 Ephemeral 32 0.5% 23680 740 2214
3 Follow list 31 0.5% 167289 5396 61894
4004 Unknown 26 0.4% 30394 1169 1169
22816 Ephemeral 26 0.4% 11154 429 429
20004 Ephemeral 26 0.4% 13878 534 549
30315 User status 26 0.4% 17657 679 872
443 Unknown 25 0.4% 43504 1740 22480
38501 Addressable 24 0.4% 17819 742 744
1985 Unknown 24 0.4% 11435 476 483
22882 Ephemeral 23 0.4% 20949 911 1455
5300 Unknown 22 0.4% 13434 611 701
31990 App handler info 22 0.4% 21777 990 6861
22790 Ephemeral 21 0.4% 8988 428 428
21050 Ephemeral 19 0.3% 17510 922 1874
10100 Unknown 19 0.3% 22108 1164 1589
20333 Ephemeral 18 0.3% 14340 797 798
30088 Addressable 16 0.3% 29798 1862 1871
6 Repost 16 0.3% 28131 1758 3369
30091 Addressable 16 0.3% 9842 615 629
6300 Unknown 15 0.3% 45643 3043 8157
22658 Ephemeral 15 0.3% 11909 794 1161
30311 Addressable 15 0.3% 16556 1104 1197
445 Encrypted group msg 15 0.3% 45070 3005 4130
10777 Unknown 14 0.2% 6840 489 489
9735 Zap receipt 14 0.2% 27231 1945 2777
4244 Unknown 12 0.2% 6768 564 564
22456 Ephemeral 12 0.2% 6808 567 790
38503 Addressable 11 0.2% 6070 552 552
10050 DM relays 11 0.2% 4882 444 592
30515 Addressable 10 0.2% 53040 5304 5304
30512 Addressable 10 0.2% 15210 1521 1596
30089 Chunked data 10 0.2% 164434 16443 44372
10000 Mute list 10 0.2% 16448 1645 4287
22809 Ephemeral 9 0.2% 3852 428 428
22486 Ephemeral 9 0.2% 3834 426 426
22376 Ephemeral 8 0.1% 3550 444 444
7368 Unknown 8 0.1% 8294 1037 1040
38500 Addressable 8 0.1% 5696 712 713
30380 Addressable 7 0.1% 3178 454 454
20000 Ephemeral 7 0.1% 2422 346 346
22104 Ephemeral 7 0.1% 3105 444 444
23025 Ephemeral 7 0.1% 3017 431 431
22896 Ephemeral 7 0.1% 3003 429 429
33334 Addressable 7 0.1% 5817 831 831
22835 Ephemeral 6 0.1% 2574 429 429
30443 Addressable 6 0.1% 7297 1216 1260
22633 Ephemeral 6 0.1% 6097 1016 1668
22392 Ephemeral 6 0.1% 2662 444 444
1984 Report 6 0.1% 3180 530 530
30064 Addressable 6 0.1% 4438 740 991
22715 Ephemeral 6 0.1% 2562 427 427
30280 Addressable 6 0.1% 3078 513 513
22559 Ephemeral 6 0.1% 2574 429 429
22813 Ephemeral 6 0.1% 2562 427 427
22635 Ephemeral 6 0.1% 2556 426 426
22746 Ephemeral 5 0.1% 2155 431 431
22364 Ephemeral 5 0.1% 2135 427 427
37195 Addressable 5 0.1% 3664 733 786
22571 Ephemeral 5 0.1% 4055 811 1127
23004 Ephemeral 5 0.1% 2145 429 429
22834 Ephemeral 5 0.1% 4050 810 1126
22636 Ephemeral 5 0.1% 3280 656 1164
22786 Ephemeral 4 0.1% 1720 430 430
25195 Ephemeral 4 0.1% 2712 678 678
4243 Unknown 4 0.1% 2256 564 564
22168 Ephemeral 4 0.1% 1774 444 444
22760 Ephemeral 4 0.1% 2239 560 695
22733 Ephemeral 4 0.1% 1696 424 424
22878 Ephemeral 3 0.1% 4059 1353 3203
22902 Ephemeral 3 0.1% 5023 1674 1675
22428 Ephemeral 3 0.1% 1331 444 444
25196 Ephemeral 3 0.1% 4938 1646 2214
22325 Ephemeral 3 0.1% 1331 444 444
4245 Unknown 3 0.1% 1332 444 444
22258 Ephemeral 3 0.1% 1331 444 444
22625 Ephemeral 3 0.1% 1284 428 428
22631 Ephemeral 3 0.1% 1281 427 427
22551 Ephemeral 3 0.1% 1816 605 695
22763 Ephemeral 3 0.1% 2988 996 1331
3927 Unknown 2 0.0% 1066 533 533
22186 Ephemeral 2 0.0% 887 444 444
30213 Addressable 2 0.0% 1264 632 632
22367 Ephemeral 2 0.0% 887 444 444
22783 Ephemeral 2 0.0% 1391 696 697
22616 Ephemeral 2 0.0% 858 429 429
22268 Ephemeral 2 0.0% 887 444 444
22279 Ephemeral 2 0.0% 887 444 444
22474 Ephemeral 2 0.0% 887 444 444
22407 Ephemeral 2 0.0% 887 444 444
22729 Ephemeral 2 0.0% 854 427 427
22234 Ephemeral 2 0.0% 887 444 444
10003 Bookmarks 2 0.0% 18782 9391 15076
30815 Addressable 2 0.0% 75068 37534 37534
1111 Comment 2 0.0% 1775 888 922
30087 Addressable 2 0.0% 13286 6643 7325
22256 Ephemeral 2 0.0% 886 443 443
22299 Ephemeral 2 0.0% 887 444 444
30090 Addressable 2 0.0% 2911 1456 1515
22260 Ephemeral 2 0.0% 887 444 444
7376 Unknown 2 0.0% 1340 670 670
22012 Ephemeral 2 0.0% 887 444 444
30618 Addressable 2 0.0% 920 460 460
1078 Unknown 2 0.0% 28397 14198 14219
22313 Ephemeral 2 0.0% 887 444 444
31339 Addressable 2 0.0% 2940 1470 1470
15750 Unknown 2 0.0% 1184 592 620
20 Picture 2 0.0% 1664 832 834
22326 Ephemeral 2 0.0% 887 444 444
22841 Ephemeral 1 0.0% 428 428 428
22738 Ephemeral 1 0.0% 1668 1668 1668
22295 Ephemeral 1 0.0% 443 443 443
22296 Ephemeral 1 0.0% 443 443 443
31510 Addressable 1 0.0% 979 979 979
22691 Ephemeral 1 0.0% 431 431 431
22340 Ephemeral 1 0.0% 443 443 443
30003 Addressable 1 0.0% 387 387 387
22197 Ephemeral 1 0.0% 443 443 443
22257 Ephemeral 1 0.0% 443 443 443
30079 Addressable 1 0.0% 875 875 875
22811 Ephemeral 1 0.0% 3211 3211 3211
38385 Addressable 1 0.0% 1350 1350 1350
22339 Ephemeral 1 0.0% 443 443 443
13196 Unknown 1 0.0% 656 656 656
20200 Ephemeral 1 0.0% 491 491 491
20201 Ephemeral 1 0.0% 491 491 491
22395 Ephemeral 1 0.0% 443 443 443
30023 Long-form article 1 0.0% 27062 27062 27062
7403 Unknown 1 0.0% 490 490 490
22618 Ephemeral 1 0.0% 1668 1668 1668
17 Unknown 1 0.0% 593 593 593
30000 Addressable 1 0.0% 823 823 823
22558 Ephemeral 1 0.0% 428 428 428
22143 Ephemeral 1 0.0% 443 443 443
22198 Ephemeral 1 0.0% 443 443 443
7375 Unknown 1 0.0% 2846 2846 2846
22506 Ephemeral 1 0.0% 426 426 426
22243 Ephemeral 1 0.0% 443 443 443
23086 Ephemeral 1 0.0% 431 431 431
22688 Ephemeral 1 0.0% 3201 3201 3201
22975 Ephemeral 1 0.0% 429 429 429
22270 Ephemeral 1 0.0% 443 443 443
22584 Ephemeral 1 0.0% 1675 1675 1675
22764 Ephemeral 1 0.0% 551 551 551
23071 Ephemeral 1 0.0% 1675 1675 1675
22817 Ephemeral 1 0.0% 559 559 559
22614 Ephemeral 1 0.0% 3200 3200 3200
22330 Ephemeral 1 0.0% 443 443 443
22229 Ephemeral 1 0.0% 443 443 443
24421 Ephemeral 1 0.0% 525 525 525
=== Raw Relay Response Log ===
# Cache-All Feasibility Test - Raw Relay Log
# Started: Sun Aug 2 16:34:39 2026
# Config: cache_all_test_primal.jsonc
# Duration: 3600 seconds
[2026-08-02 16:34:56] [RELAY] wss://relay.primal.net STATUS: connected
[2026-08-02 16:34:57] [RELAY] ? EOSE
[2026-08-02 16:38:58] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:38:58] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:39:56] [RELAY] ? EOSE
[2026-08-02 16:40:16] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:40:17] [RELAY] wss://relay.primal.net CONNECTED
[2026-08-02 16:40:17] [RELAY] wss://relay.primal.net DISCONNECTED: Connection state transitioned to 2 during poll
[2026-08-02 16:40:18] [RELAY] wss://relay.primal.net CONNECTED
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,921 @@
{
"duration_seconds": 120,
"total_events": 4267,
"total_bytes": 6726611,
"max_event_bytes": 42385,
"max_event_kind": 10002,
"unique_pubkeys": 1860,
"relay_count": 3,
"relays": [{
"url": "wss://relay.primal.net",
"events": 1670,
"eose": 1,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true,
"raw_responses": []
}, {
"url": "wss://nos.lol",
"events": 1792,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true,
"raw_responses": []
}, {
"url": "wss://relay.damus.io",
"events": 805,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true,
"raw_responses": []
}],
"kinds": [{
"kind": 21059,
"count": 1199,
"total_bytes": 3547818,
"max_bytes": 5314
}, {
"kind": 5,
"count": 466,
"total_bytes": 204587,
"max_bytes": 1064
}, {
"kind": 30078,
"count": 222,
"total_bytes": 948782,
"max_bytes": 40337
}, {
"kind": 22668,
"count": 221,
"total_bytes": 94809,
"max_bytes": 429
}, {
"kind": 20001,
"count": 152,
"total_bytes": 106536,
"max_bytes": 1755
}, {
"kind": 4515,
"count": 152,
"total_bytes": 260732,
"max_bytes": 8759
}, {
"kind": 22734,
"count": 108,
"total_bytes": 46116,
"max_bytes": 427
}, {
"kind": 1,
"count": 105,
"total_bytes": 83026,
"max_bytes": 3482
}, {
"kind": 21050,
"count": 91,
"total_bytes": 89206,
"max_bytes": 2558
}, {
"kind": 24747,
"count": 85,
"total_bytes": 42775,
"max_bytes": 671
}, {
"kind": 22850,
"count": 77,
"total_bytes": 33110,
"max_bytes": 430
}, {
"kind": 22591,
"count": 66,
"total_bytes": 53607,
"max_bytes": 1222
}, {
"kind": 38501,
"count": 61,
"total_bytes": 45175,
"max_bytes": 744
}, {
"kind": 10002,
"count": 51,
"total_bytes": 63749,
"max_bytes": 42385
}, {
"kind": 0,
"count": 47,
"total_bytes": 33672,
"max_bytes": 1611
}, {
"kind": 37195,
"count": 44,
"total_bytes": 32855,
"max_bytes": 771
}, {
"kind": 24242,
"count": 37,
"total_bytes": 21423,
"max_bytes": 579
}, {
"kind": 25050,
"count": 36,
"total_bytes": 15972,
"max_bytes": 465
}, {
"kind": 7,
"count": 36,
"total_bytes": 18965,
"max_bytes": 646
}, {
"kind": 22613,
"count": 35,
"total_bytes": 26154,
"max_bytes": 1134
}, {
"kind": 22484,
"count": 32,
"total_bytes": 29125,
"max_bytes": 1163
}, {
"kind": 22749,
"count": 29,
"total_bytes": 12441,
"max_bytes": 429
}, {
"kind": 24133,
"count": 25,
"total_bytes": 37446,
"max_bytes": 7334
}, {
"kind": 22601,
"count": 25,
"total_bytes": 10700,
"max_bytes": 428
}, {
"kind": 22507,
"count": 24,
"total_bytes": 22582,
"max_bytes": 1165
}, {
"kind": 1059,
"count": 23,
"total_bytes": 41391,
"max_bytes": 2897
}, {
"kind": 22699,
"count": 23,
"total_bytes": 9844,
"max_bytes": 428
}, {
"kind": 22676,
"count": 23,
"total_bytes": 9821,
"max_bytes": 427
}, {
"kind": 22774,
"count": 20,
"total_bytes": 8560,
"max_bytes": 428
}, {
"kind": 22795,
"count": 20,
"total_bytes": 8580,
"max_bytes": 429
}, {
"kind": 22655,
"count": 20,
"total_bytes": 8560,
"max_bytes": 428
}, {
"kind": 23003,
"count": 20,
"total_bytes": 8560,
"max_bytes": 428
}, {
"kind": 30382,
"count": 20,
"total_bytes": 9960,
"max_bytes": 498
}, {
"kind": 29333,
"count": 20,
"total_bytes": 9840,
"max_bytes": 492
}, {
"kind": 25555,
"count": 18,
"total_bytes": 62434,
"max_bytes": 17003
}, {
"kind": 11998,
"count": 18,
"total_bytes": 8322,
"max_bytes": 465
}, {
"kind": 31990,
"count": 18,
"total_bytes": 16845,
"max_bytes": 1465
}, {
"kind": 4,
"count": 18,
"total_bytes": 28064,
"max_bytes": 17018
}, {
"kind": 13194,
"count": 18,
"total_bytes": 9252,
"max_bytes": 514
}, {
"kind": 22689,
"count": 17,
"total_bytes": 7259,
"max_bytes": 427
}, {
"kind": 5300,
"count": 17,
"total_bytes": 10495,
"max_bytes": 701
}, {
"kind": 445,
"count": 17,
"total_bytes": 81892,
"max_bytes": 19620
}, {
"kind": 20000,
"count": 15,
"total_bytes": 7045,
"max_bytes": 546
}, {
"kind": 20387,
"count": 14,
"total_bytes": 7196,
"max_bytes": 514
}, {
"kind": 20004,
"count": 14,
"total_bytes": 7488,
"max_bytes": 549
}, {
"kind": 7368,
"count": 13,
"total_bytes": 13476,
"max_bytes": 1040
}, {
"kind": 22456,
"count": 13,
"total_bytes": 6110,
"max_bytes": 470
}, {
"kind": 9735,
"count": 13,
"total_bytes": 26072,
"max_bytes": 2331
}, {
"kind": 22551,
"count": 13,
"total_bytes": 11042,
"max_bytes": 1161
}, {
"kind": 22760,
"count": 13,
"total_bytes": 11014,
"max_bytes": 1161
}, {
"kind": 22658,
"count": 13,
"total_bytes": 11050,
"max_bytes": 1164
}, {
"kind": 10100,
"count": 11,
"total_bytes": 20684,
"max_bytes": 3825
}, {
"kind": 30315,
"count": 11,
"total_bytes": 8899,
"max_bytes": 1038
}, {
"kind": 20787,
"count": 11,
"total_bytes": 15458,
"max_bytes": 2474
}, {
"kind": 22236,
"count": 10,
"total_bytes": 7818,
"max_bytes": 814
}, {
"kind": 22832,
"count": 10,
"total_bytes": 4286,
"max_bytes": 429
}, {
"kind": 38502,
"count": 8,
"total_bytes": 3080,
"max_bytes": 385
}, {
"kind": 4004,
"count": 8,
"total_bytes": 9352,
"max_bytes": 1169
}, {
"kind": 22785,
"count": 8,
"total_bytes": 3432,
"max_bytes": 429
}, {
"kind": 3,
"count": 8,
"total_bytes": 35850,
"max_bytes": 9845
}, {
"kind": 1985,
"count": 8,
"total_bytes": 3792,
"max_bytes": 474
}, {
"kind": 22584,
"count": 8,
"total_bytes": 3416,
"max_bytes": 427
}, {
"kind": 22816,
"count": 8,
"total_bytes": 3432,
"max_bytes": 429
}, {
"kind": 22752,
"count": 8,
"total_bytes": 6212,
"max_bytes": 3199
}, {
"kind": 6,
"count": 7,
"total_bytes": 10112,
"max_bytes": 1872
}, {
"kind": 443,
"count": 7,
"total_bytes": 7780,
"max_bytes": 1116
}, {
"kind": 22651,
"count": 6,
"total_bytes": 2568,
"max_bytes": 428
}, {
"kind": 1077,
"count": 6,
"total_bytes": 3642,
"max_bytes": 607
}, {
"kind": 26428,
"count": 6,
"total_bytes": 5254,
"max_bytes": 876
}, {
"kind": 22616,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 22626,
"count": 6,
"total_bytes": 2568,
"max_bytes": 428
}, {
"kind": 22914,
"count": 6,
"total_bytes": 2580,
"max_bytes": 430
}, {
"kind": 22614,
"count": 6,
"total_bytes": 2568,
"max_bytes": 428
}, {
"kind": 22419,
"count": 5,
"total_bytes": 2218,
"max_bytes": 444
}, {
"kind": 22847,
"count": 5,
"total_bytes": 2155,
"max_bytes": 431
}, {
"kind": 30088,
"count": 5,
"total_bytes": 9317,
"max_bytes": 1871
}, {
"kind": 10050,
"count": 5,
"total_bytes": 2250,
"max_bytes": 450
}, {
"kind": 30091,
"count": 5,
"total_bytes": 3085,
"max_bytes": 629
}, {
"kind": 30311,
"count": 5,
"total_bytes": 5153,
"max_bytes": 1197
}, {
"kind": 23080,
"count": 5,
"total_bytes": 2155,
"max_bytes": 431
}, {
"kind": 38225,
"count": 4,
"total_bytes": 4077,
"max_bytes": 1036
}, {
"kind": 4244,
"count": 4,
"total_bytes": 2256,
"max_bytes": 564
}, {
"kind": 26429,
"count": 4,
"total_bytes": 1964,
"max_bytes": 491
}, {
"kind": 3927,
"count": 4,
"total_bytes": 2132,
"max_bytes": 533
}, {
"kind": 22806,
"count": 4,
"total_bytes": 1712,
"max_bytes": 428
}, {
"kind": 30000,
"count": 4,
"total_bytes": 9358,
"max_bytes": 2845
}, {
"kind": 21617,
"count": 4,
"total_bytes": 1980,
"max_bytes": 495
}, {
"kind": 20333,
"count": 4,
"total_bytes": 3188,
"max_bytes": 798
}, {
"kind": 22548,
"count": 4,
"total_bytes": 1720,
"max_bytes": 430
}, {
"kind": 38503,
"count": 4,
"total_bytes": 2212,
"max_bytes": 553
}, {
"kind": 30515,
"count": 4,
"total_bytes": 18496,
"max_bytes": 4624
}, {
"kind": 22740,
"count": 4,
"total_bytes": 1708,
"max_bytes": 427
}, {
"kind": 22226,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 10011,
"count": 3,
"total_bytes": 1375,
"max_bytes": 513
}, {
"kind": 30089,
"count": 3,
"total_bytes": 6944,
"max_bytes": 3412
}, {
"kind": 30023,
"count": 3,
"total_bytes": 19822,
"max_bytes": 7860
}, {
"kind": 21060,
"count": 3,
"total_bytes": 1710,
"max_bytes": 570
}, {
"kind": 22526,
"count": 3,
"total_bytes": 1290,
"max_bytes": 430
}, {
"kind": 22511,
"count": 3,
"total_bytes": 2383,
"max_bytes": 1163
}, {
"kind": 22167,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 30213,
"count": 3,
"total_bytes": 1866,
"max_bytes": 632
}, {
"kind": 22748,
"count": 3,
"total_bytes": 1284,
"max_bytes": 428
}, {
"kind": 31234,
"count": 3,
"total_bytes": 4248,
"max_bytes": 1416
}, {
"kind": 22838,
"count": 2,
"total_bytes": 860,
"max_bytes": 430
}, {
"kind": 38500,
"count": 2,
"total_bytes": 1424,
"max_bytes": 713
}, {
"kind": 38384,
"count": 2,
"total_bytes": 1486,
"max_bytes": 743
}, {
"kind": 30443,
"count": 2,
"total_bytes": 2379,
"max_bytes": 1220
}, {
"kind": 20384,
"count": 2,
"total_bytes": 1278,
"max_bytes": 639
}, {
"kind": 34237,
"count": 2,
"total_bytes": 836,
"max_bytes": 418
}, {
"kind": 22280,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 23194,
"count": 2,
"total_bytes": 1020,
"max_bytes": 510
}, {
"kind": 22320,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30080,
"count": 2,
"total_bytes": 1971,
"max_bytes": 986
}, {
"kind": 22205,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22770,
"count": 2,
"total_bytes": 862,
"max_bytes": 431
}, {
"kind": 22305,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22356,
"count": 2,
"total_bytes": 886,
"max_bytes": 443
}, {
"kind": 22611,
"count": 2,
"total_bytes": 852,
"max_bytes": 426
}, {
"kind": 22217,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22884,
"count": 2,
"total_bytes": 860,
"max_bytes": 430
}, {
"kind": 33334,
"count": 2,
"total_bytes": 1662,
"max_bytes": 831
}, {
"kind": 6300,
"count": 2,
"total_bytes": 8371,
"max_bytes": 5058
}, {
"kind": 22169,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22190,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30079,
"count": 2,
"total_bytes": 1750,
"max_bytes": 875
}, {
"kind": 39101,
"count": 2,
"total_bytes": 1066,
"max_bytes": 533
}, {
"kind": 22345,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22310,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 1111,
"count": 2,
"total_bytes": 2557,
"max_bytes": 1564
}, {
"kind": 22235,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 38385,
"count": 2,
"total_bytes": 2552,
"max_bytes": 1319
}, {
"kind": 4747,
"count": 1,
"total_bytes": 721,
"max_bytes": 721
}, {
"kind": 22844,
"count": 1,
"total_bytes": 428,
"max_bytes": 428
}, {
"kind": 22807,
"count": 1,
"total_bytes": 3205,
"max_bytes": 3205
}, {
"kind": 30066,
"count": 1,
"total_bytes": 675,
"max_bytes": 675
}, {
"kind": 4245,
"count": 1,
"total_bytes": 444,
"max_bytes": 444
}, {
"kind": 7376,
"count": 1,
"total_bytes": 521,
"max_bytes": 521
}, {
"kind": 22450,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22261,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 4243,
"count": 1,
"total_bytes": 564,
"max_bytes": 564
}, {
"kind": 22242,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 10063,
"count": 1,
"total_bytes": 415,
"max_bytes": 415
}, {
"kind": 22306,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22174,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22831,
"count": 1,
"total_bytes": 3182,
"max_bytes": 3182
}, {
"kind": 22227,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 7375,
"count": 1,
"total_bytes": 8629,
"max_bytes": 8629
}, {
"kind": 22685,
"count": 1,
"total_bytes": 553,
"max_bytes": 553
}, {
"kind": 22299,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22193,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22975,
"count": 1,
"total_bytes": 3195,
"max_bytes": 3195
}, {
"kind": 30380,
"count": 1,
"total_bytes": 454,
"max_bytes": 454
}, {
"kind": 20,
"count": 1,
"total_bytes": 860,
"max_bytes": 860
}, {
"kind": 10030,
"count": 1,
"total_bytes": 405,
"max_bytes": 405
}, {
"kind": 30087,
"count": 1,
"total_bytes": 7325,
"max_bytes": 7325
}, {
"kind": 30090,
"count": 1,
"total_bytes": 1515,
"max_bytes": 1515
}, {
"kind": 22220,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 39019,
"count": 1,
"total_bytes": 961,
"max_bytes": 961
}, {
"kind": 39015,
"count": 1,
"total_bytes": 696,
"max_bytes": 696
}, {
"kind": 30617,
"count": 1,
"total_bytes": 1089,
"max_bytes": 1089
}, {
"kind": 30618,
"count": 1,
"total_bytes": 733,
"max_bytes": 733
}, {
"kind": 31991,
"count": 1,
"total_bytes": 461,
"max_bytes": 461
}, {
"kind": 1080,
"count": 1,
"total_bytes": 949,
"max_bytes": 949
}, {
"kind": 3434,
"count": 1,
"total_bytes": 705,
"max_bytes": 705
}, {
"kind": 33333,
"count": 1,
"total_bytes": 2346,
"max_bytes": 2346
}, {
"kind": 22735,
"count": 1,
"total_bytes": 430,
"max_bytes": 430
}, {
"kind": 22415,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 21966,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 11316,
"count": 1,
"total_bytes": 725,
"max_bytes": 725
}, {
"kind": 11317,
"count": 1,
"total_bytes": 12051,
"max_bytes": 12051
}, {
"kind": 25195,
"count": 1,
"total_bytes": 678,
"max_bytes": 678
}, {
"kind": 25196,
"count": 1,
"total_bytes": 1362,
"max_bytes": 1362
}, {
"kind": 22402,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 31510,
"count": 1,
"total_bytes": 979,
"max_bytes": 979
}, {
"kind": 1078,
"count": 1,
"total_bytes": 14200,
"max_bytes": 14200
}, {
"kind": 38383,
"count": 1,
"total_bytes": 685,
"max_bytes": 685
}]
}
@@ -0,0 +1,916 @@
{
"duration_seconds": 120,
"total_events": 5012,
"total_bytes": 8091410,
"max_event_bytes": 52672,
"max_event_kind": 3,
"unique_pubkeys": 2034,
"relay_count": 3,
"relays": [{
"url": "wss://relay.primal.net",
"events": 1599,
"eose": 1,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true,
"raw_responses": []
}, {
"url": "wss://nos.lol",
"events": 2389,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true,
"raw_responses": []
}, {
"url": "wss://relay.damus.io",
"events": 1024,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true,
"raw_responses": []
}],
"kinds": [{
"kind": 21059,
"count": 1361,
"total_bytes": 4189338,
"max_bytes": 5314
}, {
"kind": 5,
"count": 579,
"total_bytes": 254363,
"max_bytes": 624
}, {
"kind": 22668,
"count": 265,
"total_bytes": 113685,
"max_bytes": 429
}, {
"kind": 30078,
"count": 190,
"total_bytes": 702178,
"max_bytes": 14343
}, {
"kind": 20001,
"count": 181,
"total_bytes": 108257,
"max_bytes": 1489
}, {
"kind": 22639,
"count": 176,
"total_bytes": 136058,
"max_bytes": 1228
}, {
"kind": 4515,
"count": 168,
"total_bytes": 319242,
"max_bytes": 14220
}, {
"kind": 24133,
"count": 152,
"total_bytes": 464764,
"max_bytes": 14162
}, {
"kind": 22734,
"count": 135,
"total_bytes": 57645,
"max_bytes": 427
}, {
"kind": 31991,
"count": 116,
"total_bytes": 53476,
"max_bytes": 461
}, {
"kind": 21050,
"count": 84,
"total_bytes": 81900,
"max_bytes": 2558
}, {
"kind": 1,
"count": 80,
"total_bytes": 58412,
"max_bytes": 1443
}, {
"kind": 22795,
"count": 76,
"total_bytes": 104000,
"max_bytes": 1848
}, {
"kind": 22850,
"count": 75,
"total_bytes": 33353,
"max_bytes": 1169
}, {
"kind": 38501,
"count": 69,
"total_bytes": 51105,
"max_bytes": 744
}, {
"kind": 22842,
"count": 55,
"total_bytes": 92475,
"max_bytes": 1849
}, {
"kind": 10002,
"count": 48,
"total_bytes": 19784,
"max_bytes": 537
}, {
"kind": 0,
"count": 47,
"total_bytes": 34961,
"max_bytes": 1356
}, {
"kind": 37195,
"count": 38,
"total_bytes": 28214,
"max_bytes": 771
}, {
"kind": 7,
"count": 38,
"total_bytes": 21600,
"max_bytes": 779
}, {
"kind": 25050,
"count": 36,
"total_bytes": 15972,
"max_bytes": 465
}, {
"kind": 24242,
"count": 34,
"total_bytes": 19686,
"max_bytes": 579
}, {
"kind": 1059,
"count": 33,
"total_bytes": 58147,
"max_bytes": 3298
}, {
"kind": 22484,
"count": 32,
"total_bytes": 29156,
"max_bytes": 1164
}, {
"kind": 22507,
"count": 28,
"total_bytes": 24308,
"max_bytes": 1165
}, {
"kind": 22613,
"count": 25,
"total_bytes": 17238,
"max_bytes": 691
}, {
"kind": 29333,
"count": 25,
"total_bytes": 12300,
"max_bytes": 492
}, {
"kind": 22601,
"count": 24,
"total_bytes": 10272,
"max_bytes": 428
}, {
"kind": 5300,
"count": 23,
"total_bytes": 14096,
"max_bytes": 701
}, {
"kind": 22751,
"count": 23,
"total_bytes": 9798,
"max_bytes": 426
}, {
"kind": 22699,
"count": 22,
"total_bytes": 9416,
"max_bytes": 428
}, {
"kind": 11998,
"count": 22,
"total_bytes": 10182,
"max_bytes": 465
}, {
"kind": 22676,
"count": 22,
"total_bytes": 9394,
"max_bytes": 427
}, {
"kind": 13194,
"count": 21,
"total_bytes": 10617,
"max_bytes": 514
}, {
"kind": 22774,
"count": 20,
"total_bytes": 8560,
"max_bytes": 428
}, {
"kind": 23003,
"count": 20,
"total_bytes": 8560,
"max_bytes": 428
}, {
"kind": 22655,
"count": 19,
"total_bytes": 8132,
"max_bytes": 428
}, {
"kind": 22543,
"count": 19,
"total_bytes": 34993,
"max_bytes": 1848
}, {
"kind": 22455,
"count": 19,
"total_bytes": 34943,
"max_bytes": 1844
}, {
"kind": 25555,
"count": 19,
"total_bytes": 94673,
"max_bytes": 17111
}, {
"kind": 30443,
"count": 19,
"total_bytes": 22379,
"max_bytes": 1260
}, {
"kind": 22777,
"count": 19,
"total_bytes": 16685,
"max_bytes": 1651
}, {
"kind": 22891,
"count": 18,
"total_bytes": 33189,
"max_bytes": 1848
}, {
"kind": 31990,
"count": 17,
"total_bytes": 15825,
"max_bytes": 1465
}, {
"kind": 20000,
"count": 16,
"total_bytes": 7206,
"max_bytes": 522
}, {
"kind": 32123,
"count": 14,
"total_bytes": 10528,
"max_bytes": 900
}, {
"kind": 22456,
"count": 13,
"total_bytes": 6110,
"max_bytes": 470
}, {
"kind": 20004,
"count": 13,
"total_bytes": 6741,
"max_bytes": 549
}, {
"kind": 22591,
"count": 13,
"total_bytes": 6348,
"max_bytes": 697
}, {
"kind": 20787,
"count": 12,
"total_bytes": 16864,
"max_bytes": 2478
}, {
"kind": 4,
"count": 12,
"total_bytes": 7972,
"max_bytes": 786
}, {
"kind": 20387,
"count": 12,
"total_bytes": 6168,
"max_bytes": 514
}, {
"kind": 38502,
"count": 12,
"total_bytes": 4620,
"max_bytes": 385
}, {
"kind": 10100,
"count": 11,
"total_bytes": 20938,
"max_bytes": 3825
}, {
"kind": 20050,
"count": 11,
"total_bytes": 9711,
"max_bytes": 891
}, {
"kind": 3,
"count": 10,
"total_bytes": 72137,
"max_bytes": 52672
}, {
"kind": 9735,
"count": 10,
"total_bytes": 20833,
"max_bytes": 2143
}, {
"kind": 445,
"count": 10,
"total_bytes": 17866,
"max_bytes": 4126
}, {
"kind": 22626,
"count": 10,
"total_bytes": 4280,
"max_bytes": 428
}, {
"kind": 7368,
"count": 9,
"total_bytes": 9343,
"max_bytes": 1041
}, {
"kind": 22736,
"count": 9,
"total_bytes": 3816,
"max_bytes": 424
}, {
"kind": 1985,
"count": 8,
"total_bytes": 3810,
"max_bytes": 483
}, {
"kind": 30091,
"count": 8,
"total_bytes": 4927,
"max_bytes": 629
}, {
"kind": 30315,
"count": 8,
"total_bytes": 4522,
"max_bytes": 872
}, {
"kind": 25055,
"count": 8,
"total_bytes": 10605,
"max_bytes": 1764
}, {
"kind": 22791,
"count": 8,
"total_bytes": 3416,
"max_bytes": 427
}, {
"kind": 22816,
"count": 8,
"total_bytes": 3432,
"max_bytes": 429
}, {
"kind": 4004,
"count": 8,
"total_bytes": 9352,
"max_bytes": 1169
}, {
"kind": 30088,
"count": 8,
"total_bytes": 14929,
"max_bytes": 1871
}, {
"kind": 31234,
"count": 8,
"total_bytes": 14048,
"max_bytes": 1756
}, {
"kind": 22813,
"count": 7,
"total_bytes": 2996,
"max_bytes": 428
}, {
"kind": 22957,
"count": 7,
"total_bytes": 3010,
"max_bytes": 430
}, {
"kind": 30089,
"count": 7,
"total_bytes": 45972,
"max_bytes": 27988
}, {
"kind": 32678,
"count": 7,
"total_bytes": 5704,
"max_bytes": 900
}, {
"kind": 22832,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 22897,
"count": 6,
"total_bytes": 2586,
"max_bytes": 431
}, {
"kind": 22549,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 22702,
"count": 6,
"total_bytes": 4753,
"max_bytes": 2618
}, {
"kind": 20333,
"count": 6,
"total_bytes": 4780,
"max_bytes": 798
}, {
"kind": 30023,
"count": 6,
"total_bytes": 21101,
"max_bytes": 5056
}, {
"kind": 22568,
"count": 5,
"total_bytes": 2135,
"max_bytes": 427
}, {
"kind": 38385,
"count": 5,
"total_bytes": 6849,
"max_bytes": 1496
}, {
"kind": 4244,
"count": 5,
"total_bytes": 2820,
"max_bytes": 564
}, {
"kind": 22717,
"count": 5,
"total_bytes": 2135,
"max_bytes": 427
}, {
"kind": 21617,
"count": 4,
"total_bytes": 1980,
"max_bytes": 495
}, {
"kind": 38225,
"count": 4,
"total_bytes": 4061,
"max_bytes": 1039
}, {
"kind": 6,
"count": 4,
"total_bytes": 8823,
"max_bytes": 3647
}, {
"kind": 22333,
"count": 4,
"total_bytes": 1773,
"max_bytes": 444
}, {
"kind": 22962,
"count": 3,
"total_bytes": 1290,
"max_bytes": 430
}, {
"kind": 33334,
"count": 3,
"total_bytes": 2229,
"max_bytes": 831
}, {
"kind": 1002,
"count": 3,
"total_bytes": 2793,
"max_bytes": 979
}, {
"kind": 22244,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22907,
"count": 3,
"total_bytes": 1287,
"max_bytes": 429
}, {
"kind": 10011,
"count": 3,
"total_bytes": 1229,
"max_bytes": 431
}, {
"kind": 22258,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22245,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 30618,
"count": 3,
"total_bytes": 1755,
"max_bytes": 733
}, {
"kind": 22296,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 38503,
"count": 3,
"total_bytes": 1659,
"max_bytes": 553
}, {
"kind": 22338,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22195,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 22342,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 10050,
"count": 3,
"total_bytes": 1350,
"max_bytes": 450
}, {
"kind": 30311,
"count": 3,
"total_bytes": 3414,
"max_bytes": 1197
}, {
"kind": 32680,
"count": 2,
"total_bytes": 1376,
"max_bytes": 688
}, {
"kind": 30380,
"count": 2,
"total_bytes": 908,
"max_bytes": 454
}, {
"kind": 22611,
"count": 2,
"total_bytes": 852,
"max_bytes": 426
}, {
"kind": 4243,
"count": 2,
"total_bytes": 1128,
"max_bytes": 564
}, {
"kind": 22526,
"count": 2,
"total_bytes": 860,
"max_bytes": 430
}, {
"kind": 22320,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 4245,
"count": 2,
"total_bytes": 888,
"max_bytes": 444
}, {
"kind": 1984,
"count": 2,
"total_bytes": 873,
"max_bytes": 446
}, {
"kind": 1111,
"count": 2,
"total_bytes": 3115,
"max_bytes": 1558
}, {
"kind": 22311,
"count": 2,
"total_bytes": 886,
"max_bytes": 443
}, {
"kind": 22662,
"count": 2,
"total_bytes": 858,
"max_bytes": 429
}, {
"kind": 22089,
"count": 2,
"total_bytes": 886,
"max_bytes": 443
}, {
"kind": 22221,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22157,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 38384,
"count": 2,
"total_bytes": 1486,
"max_bytes": 743
}, {
"kind": 20384,
"count": 2,
"total_bytes": 1278,
"max_bytes": 639
}, {
"kind": 30080,
"count": 2,
"total_bytes": 1679,
"max_bytes": 985
}, {
"kind": 39101,
"count": 2,
"total_bytes": 1066,
"max_bytes": 533
}, {
"kind": 6300,
"count": 2,
"total_bytes": 11904,
"max_bytes": 6898
}, {
"kind": 10003,
"count": 2,
"total_bytes": 14466,
"max_bytes": 7233
}, {
"kind": 30515,
"count": 2,
"total_bytes": 9248,
"max_bytes": 4624
}, {
"kind": 22829,
"count": 2,
"total_bytes": 6387,
"max_bytes": 3194
}, {
"kind": 22106,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 31927,
"count": 2,
"total_bytes": 3000,
"max_bytes": 1500
}, {
"kind": 31510,
"count": 2,
"total_bytes": 1959,
"max_bytes": 980
}, {
"kind": 3927,
"count": 2,
"total_bytes": 1066,
"max_bytes": 533
}, {
"kind": 1040,
"count": 2,
"total_bytes": 2096,
"max_bytes": 1048
}, {
"kind": 31000,
"count": 2,
"total_bytes": 1204,
"max_bytes": 616
}, {
"kind": 22201,
"count": 2,
"total_bytes": 886,
"max_bytes": 443
}, {
"kind": 22269,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22818,
"count": 2,
"total_bytes": 852,
"max_bytes": 426
}, {
"kind": 22364,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 25195,
"count": 1,
"total_bytes": 678,
"max_bytes": 678
}, {
"kind": 25196,
"count": 1,
"total_bytes": 1362,
"max_bytes": 1362
}, {
"kind": 22283,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 10030,
"count": 1,
"total_bytes": 405,
"max_bytes": 405
}, {
"kind": 22706,
"count": 1,
"total_bytes": 3192,
"max_bytes": 3192
}, {
"kind": 22236,
"count": 1,
"total_bytes": 737,
"max_bytes": 737
}, {
"kind": 31339,
"count": 1,
"total_bytes": 1470,
"max_bytes": 1470
}, {
"kind": 22651,
"count": 1,
"total_bytes": 428,
"max_bytes": 428
}, {
"kind": 38500,
"count": 1,
"total_bytes": 713,
"max_bytes": 713
}, {
"kind": 10000,
"count": 1,
"total_bytes": 1311,
"max_bytes": 1311
}, {
"kind": 31926,
"count": 1,
"total_bytes": 1038,
"max_bytes": 1038
}, {
"kind": 22757,
"count": 1,
"total_bytes": 428,
"max_bytes": 428
}, {
"kind": 39015,
"count": 1,
"total_bytes": 696,
"max_bytes": 696
}, {
"kind": 30617,
"count": 1,
"total_bytes": 1089,
"max_bytes": 1089
}, {
"kind": 22234,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30815,
"count": 1,
"total_bytes": 37534,
"max_bytes": 37534
}, {
"kind": 1078,
"count": 1,
"total_bytes": 13939,
"max_bytes": 13939
}, {
"kind": 39050,
"count": 1,
"total_bytes": 1843,
"max_bytes": 1843
}, {
"kind": 22281,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30166,
"count": 1,
"total_bytes": 808,
"max_bytes": 808
}, {
"kind": 22772,
"count": 1,
"total_bytes": 429,
"max_bytes": 429
}, {
"kind": 22356,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30554,
"count": 1,
"total_bytes": 609,
"max_bytes": 609
}, {
"kind": 38422,
"count": 1,
"total_bytes": 20025,
"max_bytes": 20025
}, {
"kind": 38000,
"count": 1,
"total_bytes": 562,
"max_bytes": 562
}, {
"kind": 22069,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 1050,
"count": 1,
"total_bytes": 437,
"max_bytes": 437
}, {
"kind": 33051,
"count": 1,
"total_bytes": 522,
"max_bytes": 522
}, {
"kind": 39036,
"count": 1,
"total_bytes": 1138,
"max_bytes": 1138
}, {
"kind": 23194,
"count": 1,
"total_bytes": 510,
"max_bytes": 510
}, {
"kind": 30081,
"count": 1,
"total_bytes": 1370,
"max_bytes": 1370
}, {
"kind": 22299,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22556,
"count": 1,
"total_bytes": 427,
"max_bytes": 427
}, {
"kind": 22302,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 39019,
"count": 1,
"total_bytes": 961,
"max_bytes": 961
}, {
"kind": 30000,
"count": 1,
"total_bytes": 823,
"max_bytes": 823
}, {
"kind": 38383,
"count": 1,
"total_bytes": 657,
"max_bytes": 657
}, {
"kind": 30087,
"count": 1,
"total_bytes": 7325,
"max_bytes": 7325
}, {
"kind": 30090,
"count": 1,
"total_bytes": 1515,
"max_bytes": 1515
}]
}
@@ -0,0 +1,281 @@
{
"duration_seconds": 0,
"total_events": 0,
"total_bytes": 0,
"max_event_bytes": 0,
"max_event_kind": 0,
"unique_pubkeys": 0,
"relay_count": 27,
"relays": [{
"url": "wss://relay.primal.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nos.lol",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.damus.io",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.ditto.pub",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.land",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.wine",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.oxtr.dev",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://spatia-arcana.com/inbox",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://eden.nostr.land",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostr.band",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://theforest.nostr1.com",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://wot.utxo.one",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://premium.primal.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.mom",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.mostr.pub",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://offchain.pub",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.divine.video",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostrplebs.com",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr-pub.wellorder.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://devrelay.azzamo.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.noderunners.network",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr-pub.semisol.dev",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.shosho.live",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostrcheck.me",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostr.info",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.momostr.pink",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://bitsat.molonlabe.holdings",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}],
"kinds": []
}
@@ -0,0 +1,161 @@
{
"duration_seconds": 0,
"total_events": 0,
"total_bytes": 0,
"max_event_bytes": 0,
"max_event_kind": 0,
"unique_pubkeys": 0,
"relay_count": 15,
"relays": [{
"url": "wss://relay.primal.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nos.lol",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.damus.io",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.land",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.ditto.pub",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.oxtr.dev",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://pyramid.fiatjaf.com/inbox",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.mostr.pub",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.divine.video",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.wine",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://theforest.nostr1.com",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostrplebs.com",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.shosho.live",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostr.band",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostr.info",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}],
"kinds": []
}
@@ -0,0 +1,121 @@
{
"duration_seconds": 0,
"total_events": 0,
"total_bytes": 0,
"max_event_bytes": 0,
"max_event_kind": 0,
"unique_pubkeys": 0,
"relay_count": 11,
"relays": [{
"url": "wss://relay.primal.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nos.lol",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.damus.io",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.ditto.pub",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.bitcoiner.social",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://nostr.land",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://pyramid.fiatjaf.com/inbox",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.divine.video",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.nostrplebs.com",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://eden.nostr.land",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}, {
"url": "wss://relay.shosho.live",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false,
"raw_responses": []
}],
"kinds": []
}
@@ -0,0 +1,834 @@
{
"duration_seconds": 114,
"total_events": 4719,
"total_bytes": 6608416,
"unique_pubkeys": 1333,
"relay_count": 15,
"relays": [{
"url": "wss://laantungir.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false
}, {
"url": "wss://nos.lol",
"events": 804,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 3,
"connected": true
}, {
"url": "wss://nostr-pub.wellorder.net",
"events": 83,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://nostr.mom",
"events": 175,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://nostr.oxtr.dev",
"events": 108,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://offchain.pub",
"events": 627,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://premium.primal.net",
"events": 1,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.damus.io",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false
}, {
"url": "wss://relay.ditto.pub",
"events": 721,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.divine.video",
"events": 3,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.momostr.pink",
"events": 86,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.mostr.pub",
"events": 836,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.nostrplebs.com",
"events": 4,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.primal.net",
"events": 1239,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://theforest.nostr1.com",
"events": 32,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}],
"kinds": [{
"kind": 21059,
"count": 675,
"total_bytes": 2192938,
"max_bytes": 5314
}, {
"kind": 30382,
"count": 563,
"total_bytes": 255591,
"max_bytes": 498
}, {
"kind": 5,
"count": 341,
"total_bytes": 153667,
"max_bytes": 1404
}, {
"kind": 22639,
"count": 231,
"total_bytes": 178011,
"max_bytes": 1230
}, {
"kind": 22551,
"count": 182,
"total_bytes": 78260,
"max_bytes": 430
}, {
"kind": 20001,
"count": 143,
"total_bytes": 70427,
"max_bytes": 1206
}, {
"kind": 30078,
"count": 141,
"total_bytes": 1361122,
"max_bytes": 44169
}, {
"kind": 0,
"count": 135,
"total_bytes": 119793,
"max_bytes": 3483
}, {
"kind": 22734,
"count": 122,
"total_bytes": 52094,
"max_bytes": 427
}, {
"kind": 24747,
"count": 101,
"total_bytes": 59352,
"max_bytes": 594
}, {
"kind": 1,
"count": 90,
"total_bytes": 92140,
"max_bytes": 4709
}, {
"kind": 22668,
"count": 88,
"total_bytes": 37752,
"max_bytes": 429
}, {
"kind": 22850,
"count": 83,
"total_bytes": 35690,
"max_bytes": 430
}, {
"kind": 22870,
"count": 76,
"total_bytes": 77061,
"max_bytes": 2202
}, {
"kind": 22951,
"count": 75,
"total_bytes": 77282,
"max_bytes": 2204
}, {
"kind": 10002,
"count": 75,
"total_bytes": 202196,
"max_bytes": 42385
}, {
"kind": 22912,
"count": 74,
"total_bytes": 31672,
"max_bytes": 428
}, {
"kind": 38501,
"count": 65,
"total_bytes": 48152,
"max_bytes": 744
}, {
"kind": 24133,
"count": 63,
"total_bytes": 72262,
"max_bytes": 2898
}, {
"kind": 6,
"count": 50,
"total_bytes": 40985,
"max_bytes": 3346
}, {
"kind": 37195,
"count": 48,
"total_bytes": 36225,
"max_bytes": 771
}, {
"kind": 21050,
"count": 48,
"total_bytes": 46800,
"max_bytes": 2558
}, {
"kind": 4515,
"count": 46,
"total_bytes": 81638,
"max_bytes": 6028
}, {
"kind": 25555,
"count": 45,
"total_bytes": 209551,
"max_bytes": 16603
}, {
"kind": 7,
"count": 40,
"total_bytes": 22486,
"max_bytes": 805
}, {
"kind": 22917,
"count": 39,
"total_bytes": 35435,
"max_bytes": 1166
}, {
"kind": 22699,
"count": 39,
"total_bytes": 26723,
"max_bytes": 1166
}, {
"kind": 22690,
"count": 39,
"total_bytes": 16770,
"max_bytes": 430
}, {
"kind": 22918,
"count": 38,
"total_bytes": 36470,
"max_bytes": 1507
}, {
"kind": 22832,
"count": 37,
"total_bytes": 16128,
"max_bytes": 446
}, {
"kind": 25050,
"count": 35,
"total_bytes": 15507,
"max_bytes": 465
}, {
"kind": 30383,
"count": 35,
"total_bytes": 18766,
"max_bytes": 541
}, {
"kind": 22713,
"count": 34,
"total_bytes": 14570,
"max_bytes": 430
}, {
"kind": 22698,
"count": 34,
"total_bytes": 31371,
"max_bytes": 1674
}, {
"kind": 22601,
"count": 28,
"total_bytes": 11984,
"max_bytes": 428
}, {
"kind": 22655,
"count": 28,
"total_bytes": 11984,
"max_bytes": 428
}, {
"kind": 1059,
"count": 23,
"total_bytes": 46912,
"max_bytes": 5285
}, {
"kind": 22751,
"count": 21,
"total_bytes": 8946,
"max_bytes": 426
}, {
"kind": 22676,
"count": 21,
"total_bytes": 8967,
"max_bytes": 427
}, {
"kind": 29333,
"count": 20,
"total_bytes": 9840,
"max_bytes": 492
}, {
"kind": 5300,
"count": 20,
"total_bytes": 11959,
"max_bytes": 630
}, {
"kind": 22743,
"count": 19,
"total_bytes": 8132,
"max_bytes": 428
}, {
"kind": 22603,
"count": 19,
"total_bytes": 8094,
"max_bytes": 426
}, {
"kind": 22693,
"count": 19,
"total_bytes": 8113,
"max_bytes": 427
}, {
"kind": 22774,
"count": 19,
"total_bytes": 8132,
"max_bytes": 428
}, {
"kind": 22808,
"count": 19,
"total_bytes": 8151,
"max_bytes": 429
}, {
"kind": 22575,
"count": 19,
"total_bytes": 8075,
"max_bytes": 425
}, {
"kind": 23003,
"count": 19,
"total_bytes": 8132,
"max_bytes": 428
}, {
"kind": 22949,
"count": 19,
"total_bytes": 8151,
"max_bytes": 429
}, {
"kind": 23334,
"count": 19,
"total_bytes": 8189,
"max_bytes": 431
}, {
"kind": 24242,
"count": 17,
"total_bytes": 9843,
"max_bytes": 579
}, {
"kind": 22816,
"count": 16,
"total_bytes": 6864,
"max_bytes": 429
}, {
"kind": 20787,
"count": 16,
"total_bytes": 25100,
"max_bytes": 2478
}, {
"kind": 13194,
"count": 15,
"total_bytes": 7574,
"max_bytes": 514
}, {
"kind": 38502,
"count": 14,
"total_bytes": 5390,
"max_bytes": 385
}, {
"kind": 22236,
"count": 14,
"total_bytes": 11214,
"max_bytes": 814
}, {
"kind": 22613,
"count": 13,
"total_bytes": 8977,
"max_bytes": 694
}, {
"kind": 30088,
"count": 12,
"total_bytes": 22340,
"max_bytes": 1871
}, {
"kind": 22484,
"count": 12,
"total_bytes": 10948,
"max_bytes": 1165
}, {
"kind": 30091,
"count": 12,
"total_bytes": 7385,
"max_bytes": 629
}, {
"kind": 20004,
"count": 11,
"total_bytes": 5643,
"max_bytes": 549
}, {
"kind": 31990,
"count": 11,
"total_bytes": 8711,
"max_bytes": 1465
}, {
"kind": 445,
"count": 10,
"total_bytes": 17972,
"max_bytes": 4126
}, {
"kind": 22795,
"count": 9,
"total_bytes": 3861,
"max_bytes": 429
}, {
"kind": 22507,
"count": 9,
"total_bytes": 8480,
"max_bytes": 1165
}, {
"kind": 22922,
"count": 9,
"total_bytes": 3870,
"max_bytes": 430
}, {
"kind": 24421,
"count": 9,
"total_bytes": 4644,
"max_bytes": 516
}, {
"kind": 22830,
"count": 9,
"total_bytes": 7317,
"max_bytes": 1168
}, {
"kind": 20000,
"count": 9,
"total_bytes": 3854,
"max_bytes": 484
}, {
"kind": 22955,
"count": 9,
"total_bytes": 3852,
"max_bytes": 428
}, {
"kind": 22793,
"count": 9,
"total_bytes": 3843,
"max_bytes": 427
}, {
"kind": 30315,
"count": 8,
"total_bytes": 4604,
"max_bytes": 926
}, {
"kind": 20387,
"count": 8,
"total_bytes": 4112,
"max_bytes": 514
}, {
"kind": 7368,
"count": 8,
"total_bytes": 8309,
"max_bytes": 1041
}, {
"kind": 4004,
"count": 8,
"total_bytes": 9352,
"max_bytes": 1169
}, {
"kind": 443,
"count": 8,
"total_bytes": 8928,
"max_bytes": 1116
}, {
"kind": 11998,
"count": 7,
"total_bytes": 3255,
"max_bytes": 465
}, {
"kind": 10100,
"count": 7,
"total_bytes": 11851,
"max_bytes": 3825
}, {
"kind": 22456,
"count": 7,
"total_bytes": 3290,
"max_bytes": 470
}, {
"kind": 30089,
"count": 7,
"total_bytes": 157322,
"max_bytes": 44372
}, {
"kind": 22737,
"count": 7,
"total_bytes": 4669,
"max_bytes": 707
}, {
"kind": 23337,
"count": 7,
"total_bytes": 3557,
"max_bytes": 509
}, {
"kind": 30515,
"count": 7,
"total_bytes": 18032,
"max_bytes": 2576
}, {
"kind": 30311,
"count": 6,
"total_bytes": 5818,
"max_bytes": 1197
}, {
"kind": 1985,
"count": 6,
"total_bytes": 2860,
"max_bytes": 482
}, {
"kind": 22859,
"count": 5,
"total_bytes": 8606,
"max_bytes": 2195
}, {
"kind": 4,
"count": 5,
"total_bytes": 3222,
"max_bytes": 722
}, {
"kind": 10011,
"count": 5,
"total_bytes": 2027,
"max_bytes": 431
}, {
"kind": 22764,
"count": 4,
"total_bytes": 1712,
"max_bytes": 428
}, {
"kind": 22777,
"count": 4,
"total_bytes": 2790,
"max_bytes": 698
}, {
"kind": 4244,
"count": 4,
"total_bytes": 2256,
"max_bytes": 564
}, {
"kind": 30023,
"count": 4,
"total_bytes": 22789,
"max_bytes": 6817
}, {
"kind": 9735,
"count": 4,
"total_bytes": 7033,
"max_bytes": 1805
}, {
"kind": 20333,
"count": 4,
"total_bytes": 3188,
"max_bytes": 798
}, {
"kind": 3,
"count": 3,
"total_bytes": 26232,
"max_bytes": 14166
}, {
"kind": 38503,
"count": 3,
"total_bytes": 1655,
"max_bytes": 552
}, {
"kind": 10050,
"count": 3,
"total_bytes": 1411,
"max_bytes": 511
}, {
"kind": 21617,
"count": 3,
"total_bytes": 1485,
"max_bytes": 495
}, {
"kind": 1984,
"count": 3,
"total_bytes": 1986,
"max_bytes": 662
}, {
"kind": 3927,
"count": 3,
"total_bytes": 1599,
"max_bytes": 533
}, {
"kind": 3434,
"count": 3,
"total_bytes": 1907,
"max_bytes": 665
}, {
"kind": 22514,
"count": 2,
"total_bytes": 856,
"max_bytes": 428
}, {
"kind": 33334,
"count": 2,
"total_bytes": 1662,
"max_bytes": 831
}, {
"kind": 38500,
"count": 2,
"total_bytes": 1424,
"max_bytes": 713
}, {
"kind": 22526,
"count": 2,
"total_bytes": 860,
"max_bytes": 430
}, {
"kind": 30380,
"count": 2,
"total_bytes": 908,
"max_bytes": 454
}, {
"kind": 23194,
"count": 2,
"total_bytes": 1020,
"max_bytes": 510
}, {
"kind": 23042,
"count": 2,
"total_bytes": 858,
"max_bytes": 429
}, {
"kind": 38385,
"count": 2,
"total_bytes": 2692,
"max_bytes": 1478
}, {
"kind": 30443,
"count": 2,
"total_bytes": 2369,
"max_bytes": 1219
}, {
"kind": 22747,
"count": 2,
"total_bytes": 2064,
"max_bytes": 1288
}, {
"kind": 4245,
"count": 2,
"total_bytes": 888,
"max_bytes": 444
}, {
"kind": 1111,
"count": 2,
"total_bytes": 1936,
"max_bytes": 981
}, {
"kind": 38383,
"count": 2,
"total_bytes": 1439,
"max_bytes": 720
}, {
"kind": 4243,
"count": 2,
"total_bytes": 1128,
"max_bytes": 564
}, {
"kind": 38225,
"count": 1,
"total_bytes": 978,
"max_bytes": 978
}, {
"kind": 30618,
"count": 1,
"total_bytes": 733,
"max_bytes": 733
}, {
"kind": 31510,
"count": 1,
"total_bytes": 981,
"max_bytes": 981
}, {
"kind": 30617,
"count": 1,
"total_bytes": 1089,
"max_bytes": 1089
}, {
"kind": 1077,
"count": 1,
"total_bytes": 607,
"max_bytes": 607
}, {
"kind": 20201,
"count": 1,
"total_bytes": 491,
"max_bytes": 491
}, {
"kind": 20200,
"count": 1,
"total_bytes": 491,
"max_bytes": 491
}, {
"kind": 1078,
"count": 1,
"total_bytes": 14173,
"max_bytes": 14173
}, {
"kind": 6300,
"count": 1,
"total_bytes": 3698,
"max_bytes": 3698
}, {
"kind": 11316,
"count": 1,
"total_bytes": 791,
"max_bytes": 791
}, {
"kind": 11317,
"count": 1,
"total_bytes": 2873,
"max_bytes": 2873
}, {
"kind": 17,
"count": 1,
"total_bytes": 610,
"max_bytes": 610
}, {
"kind": 23059,
"count": 1,
"total_bytes": 429,
"max_bytes": 429
}, {
"kind": 22806,
"count": 1,
"total_bytes": 1520,
"max_bytes": 1520
}, {
"kind": 22120,
"count": 1,
"total_bytes": 1127,
"max_bytes": 1127
}, {
"kind": 13196,
"count": 1,
"total_bytes": 656,
"max_bytes": 656
}, {
"kind": 24913,
"count": 1,
"total_bytes": 431,
"max_bytes": 431
}, {
"kind": 12222,
"count": 1,
"total_bytes": 579,
"max_bytes": 579
}, {
"kind": 1040,
"count": 1,
"total_bytes": 1048,
"max_bytes": 1048
}, {
"kind": 22119,
"count": 1,
"total_bytes": 845,
"max_bytes": 845
}, {
"kind": 10312,
"count": 1,
"total_bytes": 472,
"max_bytes": 472
}, {
"kind": 3079,
"count": 1,
"total_bytes": 909,
"max_bytes": 909
}, {
"kind": 30000,
"count": 1,
"total_bytes": 594,
"max_bytes": 594
}]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,704 @@
{
"duration_seconds": 32,
"total_events": 1485,
"total_bytes": 1812166,
"unique_pubkeys": 511,
"relay_count": 15,
"relays": [{
"url": "wss://laantungir.net",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": false
}, {
"url": "wss://nos.lol",
"events": 247,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://nostr-pub.wellorder.net",
"events": 37,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://nostr.mom",
"events": 38,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://nostr.oxtr.dev",
"events": 36,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://offchain.pub",
"events": 113,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://premium.primal.net",
"events": 1,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.damus.io",
"events": 194,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.ditto.pub",
"events": 277,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 1,
"connected": true
}, {
"url": "wss://relay.divine.video",
"events": 2,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.momostr.pink",
"events": 27,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.mostr.pub",
"events": 148,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.nostrplebs.com",
"events": 0,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://relay.primal.net",
"events": 360,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}, {
"url": "wss://theforest.nostr1.com",
"events": 5,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 0,
"connected": true
}],
"kinds": [{
"kind": 21059,
"count": 184,
"total_bytes": 623532,
"max_bytes": 5314
}, {
"kind": 22591,
"count": 139,
"total_bytes": 107560,
"max_bytes": 1223
}, {
"kind": 30382,
"count": 119,
"total_bytes": 53663,
"max_bytes": 458
}, {
"kind": 30383,
"count": 119,
"total_bytes": 53605,
"max_bytes": 536
}, {
"kind": 5,
"count": 91,
"total_bytes": 39950,
"max_bytes": 624
}, {
"kind": 30078,
"count": 54,
"total_bytes": 179695,
"max_bytes": 14343
}, {
"kind": 20001,
"count": 49,
"total_bytes": 35635,
"max_bytes": 1871
}, {
"kind": 0,
"count": 48,
"total_bytes": 39906,
"max_bytes": 1468
}, {
"kind": 22668,
"count": 48,
"total_bytes": 20592,
"max_bytes": 429
}, {
"kind": 1,
"count": 45,
"total_bytes": 38547,
"max_bytes": 2413
}, {
"kind": 22734,
"count": 26,
"total_bytes": 11102,
"max_bytes": 427
}, {
"kind": 4515,
"count": 26,
"total_bytes": 38218,
"max_bytes": 2956
}, {
"kind": 10002,
"count": 25,
"total_bytes": 11707,
"max_bytes": 565
}, {
"kind": 22850,
"count": 24,
"total_bytes": 10320,
"max_bytes": 430
}, {
"kind": 7,
"count": 24,
"total_bytes": 13528,
"max_bytes": 780
}, {
"kind": 22551,
"count": 24,
"total_bytes": 10320,
"max_bytes": 430
}, {
"kind": 31991,
"count": 20,
"total_bytes": 9220,
"max_bytes": 461
}, {
"kind": 38501,
"count": 18,
"total_bytes": 13341,
"max_bytes": 744
}, {
"kind": 22663,
"count": 17,
"total_bytes": 7276,
"max_bytes": 428
}, {
"kind": 25555,
"count": 15,
"total_bytes": 90493,
"max_bytes": 16887
}, {
"kind": 22912,
"count": 13,
"total_bytes": 5564,
"max_bytes": 428
}, {
"kind": 1059,
"count": 13,
"total_bytes": 23775,
"max_bytes": 1883
}, {
"kind": 22832,
"count": 12,
"total_bytes": 5233,
"max_bytes": 446
}, {
"kind": 22690,
"count": 11,
"total_bytes": 4730,
"max_bytes": 430
}, {
"kind": 22545,
"count": 11,
"total_bytes": 4697,
"max_bytes": 427
}, {
"kind": 25050,
"count": 10,
"total_bytes": 4394,
"max_bytes": 465
}, {
"kind": 37195,
"count": 10,
"total_bytes": 7494,
"max_bytes": 771
}, {
"kind": 22601,
"count": 10,
"total_bytes": 4280,
"max_bytes": 428
}, {
"kind": 22795,
"count": 10,
"total_bytes": 11639,
"max_bytes": 1487
}, {
"kind": 22891,
"count": 9,
"total_bytes": 13331,
"max_bytes": 1490
}, {
"kind": 22655,
"count": 9,
"total_bytes": 3852,
"max_bytes": 428
}, {
"kind": 22911,
"count": 9,
"total_bytes": 13328,
"max_bytes": 1487
}, {
"kind": 24133,
"count": 9,
"total_bytes": 8250,
"max_bytes": 1534
}, {
"kind": 13194,
"count": 8,
"total_bytes": 4112,
"max_bytes": 514
}, {
"kind": 22647,
"count": 8,
"total_bytes": 3416,
"max_bytes": 427
}, {
"kind": 22695,
"count": 7,
"total_bytes": 10339,
"max_bytes": 1481
}, {
"kind": 22653,
"count": 7,
"total_bytes": 10368,
"max_bytes": 1487
}, {
"kind": 22455,
"count": 7,
"total_bytes": 10339,
"max_bytes": 1485
}, {
"kind": 22543,
"count": 7,
"total_bytes": 10354,
"max_bytes": 1487
}, {
"kind": 30315,
"count": 7,
"total_bytes": 2714,
"max_bytes": 398
}, {
"kind": 22676,
"count": 6,
"total_bytes": 2562,
"max_bytes": 427
}, {
"kind": 22575,
"count": 6,
"total_bytes": 2550,
"max_bytes": 425
}, {
"kind": 22689,
"count": 6,
"total_bytes": 8851,
"max_bytes": 1484
}, {
"kind": 22913,
"count": 6,
"total_bytes": 2586,
"max_bytes": 431
}, {
"kind": 22774,
"count": 6,
"total_bytes": 2568,
"max_bytes": 428
}, {
"kind": 24242,
"count": 6,
"total_bytes": 3474,
"max_bytes": 579
}, {
"kind": 22808,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 22842,
"count": 6,
"total_bytes": 8891,
"max_bytes": 1488
}, {
"kind": 22603,
"count": 5,
"total_bytes": 2130,
"max_bytes": 426
}, {
"kind": 6,
"count": 5,
"total_bytes": 10814,
"max_bytes": 7298
}, {
"kind": 31990,
"count": 5,
"total_bytes": 4305,
"max_bytes": 1321
}, {
"kind": 22384,
"count": 5,
"total_bytes": 2130,
"max_bytes": 426
}, {
"kind": 22699,
"count": 5,
"total_bytes": 2140,
"max_bytes": 428
}, {
"kind": 22613,
"count": 5,
"total_bytes": 3443,
"max_bytes": 691
}, {
"kind": 38502,
"count": 5,
"total_bytes": 1925,
"max_bytes": 385
}, {
"kind": 23003,
"count": 5,
"total_bytes": 2140,
"max_bytes": 428
}, {
"kind": 22743,
"count": 5,
"total_bytes": 2140,
"max_bytes": 428
}, {
"kind": 29333,
"count": 5,
"total_bytes": 2460,
"max_bytes": 492
}, {
"kind": 20004,
"count": 4,
"total_bytes": 1800,
"max_bytes": 549
}, {
"kind": 4,
"count": 4,
"total_bytes": 3072,
"max_bytes": 826
}, {
"kind": 20387,
"count": 4,
"total_bytes": 2056,
"max_bytes": 514
}, {
"kind": 22816,
"count": 4,
"total_bytes": 1716,
"max_bytes": 429
}, {
"kind": 20000,
"count": 4,
"total_bytes": 1872,
"max_bytes": 628
}, {
"kind": 22934,
"count": 3,
"total_bytes": 5535,
"max_bytes": 1846
}, {
"kind": 1985,
"count": 2,
"total_bytes": 948,
"max_bytes": 474
}, {
"kind": 31234,
"count": 2,
"total_bytes": 89092,
"max_bytes": 44546
}, {
"kind": 10777,
"count": 2,
"total_bytes": 976,
"max_bytes": 488
}, {
"kind": 4004,
"count": 2,
"total_bytes": 2338,
"max_bytes": 1169
}, {
"kind": 9735,
"count": 2,
"total_bytes": 3926,
"max_bytes": 2135
}, {
"kind": 1000,
"count": 2,
"total_bytes": 3415,
"max_bytes": 1712
}, {
"kind": 25195,
"count": 2,
"total_bytes": 1356,
"max_bytes": 678
}, {
"kind": 30311,
"count": 2,
"total_bytes": 2210,
"max_bytes": 1198
}, {
"kind": 25196,
"count": 2,
"total_bytes": 3576,
"max_bytes": 2214
}, {
"kind": 1040,
"count": 2,
"total_bytes": 1920,
"max_bytes": 960
}, {
"kind": 38383,
"count": 2,
"total_bytes": 1532,
"max_bytes": 815
}, {
"kind": 30515,
"count": 2,
"total_bytes": 5152,
"max_bytes": 2576
}, {
"kind": 30091,
"count": 2,
"total_bytes": 1221,
"max_bytes": 611
}, {
"kind": 30088,
"count": 2,
"total_bytes": 3717,
"max_bytes": 1859
}, {
"kind": 21617,
"count": 1,
"total_bytes": 494,
"max_bytes": 494
}, {
"kind": 30380,
"count": 1,
"total_bytes": 454,
"max_bytes": 454
}, {
"kind": 22611,
"count": 1,
"total_bytes": 426,
"max_bytes": 426
}, {
"kind": 30079,
"count": 1,
"total_bytes": 875,
"max_bytes": 875
}, {
"kind": 22767,
"count": 1,
"total_bytes": 429,
"max_bytes": 429
}, {
"kind": 38500,
"count": 1,
"total_bytes": 711,
"max_bytes": 711
}, {
"kind": 30312,
"count": 1,
"total_bytes": 461,
"max_bytes": 461
}, {
"kind": 7368,
"count": 1,
"total_bytes": 1039,
"max_bytes": 1039
}, {
"kind": 22967,
"count": 1,
"total_bytes": 431,
"max_bytes": 431
}, {
"kind": 39101,
"count": 1,
"total_bytes": 533,
"max_bytes": 533
}, {
"kind": 445,
"count": 1,
"total_bytes": 4130,
"max_bytes": 4130
}, {
"kind": 10000,
"count": 1,
"total_bytes": 1722,
"max_bytes": 1722
}, {
"kind": 30617,
"count": 1,
"total_bytes": 1089,
"max_bytes": 1089
}, {
"kind": 22526,
"count": 1,
"total_bytes": 430,
"max_bytes": 430
}, {
"kind": 20787,
"count": 1,
"total_bytes": 926,
"max_bytes": 926
}, {
"kind": 443,
"count": 1,
"total_bytes": 800,
"max_bytes": 800
}, {
"kind": 16,
"count": 1,
"total_bytes": 780,
"max_bytes": 780
}, {
"kind": 10011,
"count": 1,
"total_bytes": 397,
"max_bytes": 397
}, {
"kind": 22679,
"count": 1,
"total_bytes": 693,
"max_bytes": 693
}, {
"kind": 23042,
"count": 1,
"total_bytes": 429,
"max_bytes": 429
}, {
"kind": 22514,
"count": 1,
"total_bytes": 428,
"max_bytes": 428
}, {
"kind": 30023,
"count": 1,
"total_bytes": 8164,
"max_bytes": 8164
}, {
"kind": 5300,
"count": 1,
"total_bytes": 630,
"max_bytes": 630
}, {
"kind": 22605,
"count": 1,
"total_bytes": 692,
"max_bytes": 692
}, {
"kind": 34237,
"count": 1,
"total_bytes": 418,
"max_bytes": 418
}, {
"kind": 30618,
"count": 1,
"total_bytes": 733,
"max_bytes": 733
}, {
"kind": 38421,
"count": 1,
"total_bytes": 1069,
"max_bytes": 1069
}, {
"kind": 10100,
"count": 1,
"total_bytes": 1036,
"max_bytes": 1036
}, {
"kind": 30080,
"count": 1,
"total_bytes": 925,
"max_bytes": 925
}, {
"kind": 38385,
"count": 1,
"total_bytes": 1214,
"max_bytes": 1214
}, {
"kind": 38225,
"count": 1,
"total_bytes": 1012,
"max_bytes": 1012
}, {
"kind": 11998,
"count": 1,
"total_bytes": 465,
"max_bytes": 465
}, {
"kind": 22236,
"count": 1,
"total_bytes": 747,
"max_bytes": 747
}, {
"kind": 22823,
"count": 1,
"total_bytes": 1852,
"max_bytes": 1852
}]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,998 @@
{
"duration_seconds": 503,
"total_events": 7884,
"total_bytes": 12205231,
"unique_pubkeys": 2301,
"relay_count": 1,
"relays": [{
"url": "wss://relay.primal.net",
"events": 7884,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 10,
"connected": true
}],
"kinds": [{
"kind": 1059,
"count": 871,
"total_bytes": 1657832,
"max_bytes": 55121
}, {
"kind": 21059,
"count": 740,
"total_bytes": 2308644,
"max_bytes": 22354
}, {
"kind": 30078,
"count": 622,
"total_bytes": 2651879,
"max_bytes": 55090
}, {
"kind": 30383,
"count": 520,
"total_bytes": 278610,
"max_bytes": 548
}, {
"kind": 20001,
"count": 386,
"total_bytes": 403111,
"max_bytes": 2026
}, {
"kind": 22734,
"count": 357,
"total_bytes": 152451,
"max_bytes": 428
}, {
"kind": 1,
"count": 303,
"total_bytes": 303990,
"max_bytes": 4648
}, {
"kind": 30382,
"count": 288,
"total_bytes": 137207,
"max_bytes": 484
}, {
"kind": 0,
"count": 189,
"total_bytes": 140594,
"max_bytes": 2012
}, {
"kind": 29333,
"count": 165,
"total_bytes": 81180,
"max_bytes": 492
}, {
"kind": 10002,
"count": 158,
"total_bytes": 571188,
"max_bytes": 42385
}, {
"kind": 22236,
"count": 158,
"total_bytes": 120802,
"max_bytes": 801
}, {
"kind": 5,
"count": 154,
"total_bytes": 73847,
"max_bytes": 664
}, {
"kind": 4515,
"count": 154,
"total_bytes": 463562,
"max_bytes": 38797
}, {
"kind": 22743,
"count": 134,
"total_bytes": 57352,
"max_bytes": 428
}, {
"kind": 7,
"count": 113,
"total_bytes": 62452,
"max_bytes": 727
}, {
"kind": 24133,
"count": 102,
"total_bytes": 132440,
"max_bytes": 7334
}, {
"kind": 22684,
"count": 99,
"total_bytes": 42669,
"max_bytes": 431
}, {
"kind": 22850,
"count": 94,
"total_bytes": 40420,
"max_bytes": 430
}, {
"kind": 22838,
"count": 88,
"total_bytes": 37752,
"max_bytes": 429
}, {
"kind": 22774,
"count": 84,
"total_bytes": 35952,
"max_bytes": 428
}, {
"kind": 23003,
"count": 84,
"total_bytes": 35952,
"max_bytes": 428
}, {
"kind": 22876,
"count": 83,
"total_bytes": 35358,
"max_bytes": 426
}, {
"kind": 22611,
"count": 75,
"total_bytes": 32175,
"max_bytes": 429
}, {
"kind": 25555,
"count": 69,
"total_bytes": 281319,
"max_bytes": 16987
}, {
"kind": 4,
"count": 66,
"total_bytes": 72028,
"max_bytes": 17018
}, {
"kind": 22613,
"count": 63,
"total_bytes": 43489,
"max_bytes": 692
}, {
"kind": 25050,
"count": 61,
"total_bytes": 24461,
"max_bytes": 401
}, {
"kind": 11998,
"count": 61,
"total_bytes": 28269,
"max_bytes": 465
}, {
"kind": 443,
"count": 49,
"total_bytes": 68056,
"max_bytes": 22480
}, {
"kind": 30301,
"count": 47,
"total_bytes": 53284,
"max_bytes": 1912
}, {
"kind": 3927,
"count": 47,
"total_bytes": 25051,
"max_bytes": 533
}, {
"kind": 20387,
"count": 44,
"total_bytes": 22616,
"max_bytes": 514
}, {
"kind": 22761,
"count": 44,
"total_bytes": 18832,
"max_bytes": 428
}, {
"kind": 13194,
"count": 43,
"total_bytes": 22043,
"max_bytes": 514
}, {
"kind": 20004,
"count": 42,
"total_bytes": 22266,
"max_bytes": 549
}, {
"kind": 5300,
"count": 42,
"total_bytes": 25437,
"max_bytes": 637
}, {
"kind": 22546,
"count": 39,
"total_bytes": 16614,
"max_bytes": 426
}, {
"kind": 22714,
"count": 37,
"total_bytes": 15836,
"max_bytes": 428
}, {
"kind": 30166,
"count": 35,
"total_bytes": 17904,
"max_bytes": 589
}, {
"kind": 9735,
"count": 35,
"total_bytes": 70344,
"max_bytes": 3538
}, {
"kind": 23334,
"count": 35,
"total_bytes": 15084,
"max_bytes": 432
}, {
"kind": 1985,
"count": 34,
"total_bytes": 16232,
"max_bytes": 483
}, {
"kind": 22816,
"count": 33,
"total_bytes": 14157,
"max_bytes": 429
}, {
"kind": 21050,
"count": 33,
"total_bytes": 30862,
"max_bytes": 1874
}, {
"kind": 30315,
"count": 32,
"total_bytes": 21166,
"max_bytes": 925
}, {
"kind": 445,
"count": 32,
"total_bytes": 120956,
"max_bytes": 9346
}, {
"kind": 22698,
"count": 32,
"total_bytes": 13664,
"max_bytes": 427
}, {
"kind": 4004,
"count": 32,
"total_bytes": 37408,
"max_bytes": 1169
}, {
"kind": 38501,
"count": 31,
"total_bytes": 23034,
"max_bytes": 744
}, {
"kind": 31990,
"count": 25,
"total_bytes": 9675,
"max_bytes": 387
}, {
"kind": 10100,
"count": 24,
"total_bytes": 27687,
"max_bytes": 1589
}, {
"kind": 20333,
"count": 24,
"total_bytes": 19120,
"max_bytes": 798
}, {
"kind": 3,
"count": 23,
"total_bytes": 328284,
"max_bytes": 56750
}, {
"kind": 24913,
"count": 22,
"total_bytes": 9700,
"max_bytes": 493
}, {
"kind": 30311,
"count": 21,
"total_bytes": 22061,
"max_bytes": 1197
}, {
"kind": 6,
"count": 21,
"total_bytes": 49793,
"max_bytes": 12925
}, {
"kind": 10050,
"count": 19,
"total_bytes": 8172,
"max_bytes": 450
}, {
"kind": 22712,
"count": 18,
"total_bytes": 7704,
"max_bytes": 428
}, {
"kind": 22808,
"count": 17,
"total_bytes": 7293,
"max_bytes": 429
}, {
"kind": 10000,
"count": 16,
"total_bytes": 26659,
"max_bytes": 12630
}, {
"kind": 4244,
"count": 15,
"total_bytes": 8460,
"max_bytes": 564
}, {
"kind": 23025,
"count": 15,
"total_bytes": 6465,
"max_bytes": 431
}, {
"kind": 31234,
"count": 14,
"total_bytes": 24051,
"max_bytes": 2268
}, {
"kind": 38503,
"count": 13,
"total_bytes": 7174,
"max_bytes": 552
}, {
"kind": 22630,
"count": 12,
"total_bytes": 11762,
"max_bytes": 1654
}, {
"kind": 37195,
"count": 12,
"total_bytes": 8752,
"max_bytes": 934
}, {
"kind": 6300,
"count": 12,
"total_bytes": 77247,
"max_bytes": 17248
}, {
"kind": 10777,
"count": 12,
"total_bytes": 5861,
"max_bytes": 489
}, {
"kind": 30091,
"count": 11,
"total_bytes": 6763,
"max_bytes": 629
}, {
"kind": 20000,
"count": 11,
"total_bytes": 3806,
"max_bytes": 346
}, {
"kind": 30515,
"count": 11,
"total_bytes": 58344,
"max_bytes": 5304
}, {
"kind": 30088,
"count": 11,
"total_bytes": 20483,
"max_bytes": 1871
}, {
"kind": 22361,
"count": 10,
"total_bytes": 4439,
"max_bytes": 445
}, {
"kind": 30280,
"count": 9,
"total_bytes": 4617,
"max_bytes": 513
}, {
"kind": 22902,
"count": 9,
"total_bytes": 3861,
"max_bytes": 429
}, {
"kind": 22506,
"count": 8,
"total_bytes": 3416,
"max_bytes": 427
}, {
"kind": 7368,
"count": 8,
"total_bytes": 8304,
"max_bytes": 1040
}, {
"kind": 22645,
"count": 8,
"total_bytes": 3432,
"max_bytes": 429
}, {
"kind": 38500,
"count": 8,
"total_bytes": 5696,
"max_bytes": 713
}, {
"kind": 33334,
"count": 8,
"total_bytes": 6648,
"max_bytes": 831
}, {
"kind": 30380,
"count": 8,
"total_bytes": 3632,
"max_bytes": 454
}, {
"kind": 22688,
"count": 7,
"total_bytes": 2989,
"max_bytes": 427
}, {
"kind": 22656,
"count": 7,
"total_bytes": 3017,
"max_bytes": 431
}, {
"kind": 22316,
"count": 7,
"total_bytes": 3106,
"max_bytes": 444
}, {
"kind": 30089,
"count": 6,
"total_bytes": 123676,
"max_bytes": 44372
}, {
"kind": 22456,
"count": 6,
"total_bytes": 3140,
"max_bytes": 790
}, {
"kind": 22739,
"count": 6,
"total_bytes": 2580,
"max_bytes": 430
}, {
"kind": 22882,
"count": 6,
"total_bytes": 5292,
"max_bytes": 1449
}, {
"kind": 22862,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 1111,
"count": 6,
"total_bytes": 7227,
"max_bytes": 1698
}, {
"kind": 4245,
"count": 5,
"total_bytes": 2220,
"max_bytes": 444
}, {
"kind": 30512,
"count": 5,
"total_bytes": 7504,
"max_bytes": 1506
}, {
"kind": 22641,
"count": 5,
"total_bytes": 2145,
"max_bytes": 429
}, {
"kind": 22586,
"count": 5,
"total_bytes": 2135,
"max_bytes": 427
}, {
"kind": 4243,
"count": 5,
"total_bytes": 2820,
"max_bytes": 564
}, {
"kind": 22835,
"count": 5,
"total_bytes": 2145,
"max_bytes": 429
}, {
"kind": 22620,
"count": 5,
"total_bytes": 2145,
"max_bytes": 429
}, {
"kind": 22298,
"count": 5,
"total_bytes": 2218,
"max_bytes": 444
}, {
"kind": 22852,
"count": 5,
"total_bytes": 2140,
"max_bytes": 428
}, {
"kind": 22726,
"count": 5,
"total_bytes": 2150,
"max_bytes": 430
}, {
"kind": 22362,
"count": 5,
"total_bytes": 2218,
"max_bytes": 444
}, {
"kind": 22772,
"count": 5,
"total_bytes": 4051,
"max_bytes": 1132
}, {
"kind": 22805,
"count": 4,
"total_bytes": 1712,
"max_bytes": 428
}, {
"kind": 9734,
"count": 4,
"total_bytes": 3756,
"max_bytes": 1033
}, {
"kind": 22200,
"count": 4,
"total_bytes": 1774,
"max_bytes": 444
}, {
"kind": 31510,
"count": 4,
"total_bytes": 3922,
"max_bytes": 982
}, {
"kind": 22570,
"count": 4,
"total_bytes": 1716,
"max_bytes": 429
}, {
"kind": 25195,
"count": 4,
"total_bytes": 2712,
"max_bytes": 678
}, {
"kind": 22752,
"count": 3,
"total_bytes": 4047,
"max_bytes": 3191
}, {
"kind": 31339,
"count": 3,
"total_bytes": 4410,
"max_bytes": 1470
}, {
"kind": 22949,
"count": 3,
"total_bytes": 1287,
"max_bytes": 429
}, {
"kind": 22358,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 22474,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22185,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22936,
"count": 3,
"total_bytes": 1290,
"max_bytes": 430
}, {
"kind": 25196,
"count": 3,
"total_bytes": 4938,
"max_bytes": 2214
}, {
"kind": 22439,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22824,
"count": 3,
"total_bytes": 1281,
"max_bytes": 427
}, {
"kind": 22270,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22263,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 22755,
"count": 3,
"total_bytes": 1284,
"max_bytes": 428
}, {
"kind": 10051,
"count": 3,
"total_bytes": 1317,
"max_bytes": 439
}, {
"kind": 34775,
"count": 3,
"total_bytes": 7599,
"max_bytes": 2561
}, {
"kind": 30300,
"count": 3,
"total_bytes": 5502,
"max_bytes": 1834
}, {
"kind": 22234,
"count": 3,
"total_bytes": 1330,
"max_bytes": 444
}, {
"kind": 7403,
"count": 2,
"total_bytes": 978,
"max_bytes": 489
}, {
"kind": 22626,
"count": 2,
"total_bytes": 1916,
"max_bytes": 1486
}, {
"kind": 30079,
"count": 2,
"total_bytes": 1750,
"max_bytes": 875
}, {
"kind": 15750,
"count": 2,
"total_bytes": 1242,
"max_bytes": 626
}, {
"kind": 23337,
"count": 2,
"total_bytes": 1027,
"max_bytes": 514
}, {
"kind": 30815,
"count": 2,
"total_bytes": 75068,
"max_bytes": 37534
}, {
"kind": 30213,
"count": 2,
"total_bytes": 1264,
"max_bytes": 632
}, {
"kind": 22716,
"count": 2,
"total_bytes": 854,
"max_bytes": 427
}, {
"kind": 22492,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30000,
"count": 2,
"total_bytes": 1646,
"max_bytes": 823
}, {
"kind": 22463,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22304,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 10003,
"count": 2,
"total_bytes": 20016,
"max_bytes": 15003
}, {
"kind": 30443,
"count": 2,
"total_bytes": 2572,
"max_bytes": 1312
}, {
"kind": 22233,
"count": 2,
"total_bytes": 886,
"max_bytes": 443
}, {
"kind": 22275,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30087,
"count": 2,
"total_bytes": 11238,
"max_bytes": 7325
}, {
"kind": 20078,
"count": 2,
"total_bytes": 1458,
"max_bytes": 729
}, {
"kind": 22057,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22251,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22181,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22678,
"count": 2,
"total_bytes": 1388,
"max_bytes": 694
}, {
"kind": 22747,
"count": 2,
"total_bytes": 858,
"max_bytes": 429
}, {
"kind": 22410,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22334,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22249,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22258,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30090,
"count": 2,
"total_bytes": 2588,
"max_bytes": 1515
}, {
"kind": 10312,
"count": 1,
"total_bytes": 472,
"max_bytes": 472
}, {
"kind": 22853,
"count": 1,
"total_bytes": 1673,
"max_bytes": 1673
}, {
"kind": 22303,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22306,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 20200,
"count": 1,
"total_bytes": 491,
"max_bytes": 491
}, {
"kind": 22132,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 20201,
"count": 1,
"total_bytes": 491,
"max_bytes": 491
}, {
"kind": 22245,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22352,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22240,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 34236,
"count": 1,
"total_bytes": 931,
"max_bytes": 931
}, {
"kind": 17,
"count": 1,
"total_bytes": 610,
"max_bytes": 610
}, {
"kind": 22286,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 13196,
"count": 1,
"total_bytes": 656,
"max_bytes": 656
}, {
"kind": 20242,
"count": 1,
"total_bytes": 346,
"max_bytes": 346
}, {
"kind": 7376,
"count": 1,
"total_bytes": 670,
"max_bytes": 670
}, {
"kind": 38385,
"count": 1,
"total_bytes": 1350,
"max_bytes": 1350
}, {
"kind": 22355,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 23333,
"count": 1,
"total_bytes": 571,
"max_bytes": 571
}, {
"kind": 22737,
"count": 1,
"total_bytes": 556,
"max_bytes": 556
}, {
"kind": 22291,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30421,
"count": 1,
"total_bytes": 995,
"max_bytes": 995
}, {
"kind": 22860,
"count": 1,
"total_bytes": 553,
"max_bytes": 553
}, {
"kind": 2333,
"count": 1,
"total_bytes": 659,
"max_bytes": 659
}, {
"kind": 22219,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22382,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 23032,
"count": 1,
"total_bytes": 430,
"max_bytes": 430
}, {
"kind": 7375,
"count": 1,
"total_bytes": 1822,
"max_bytes": 1822
}, {
"kind": 22579,
"count": 1,
"total_bytes": 1670,
"max_bytes": 1670
}, {
"kind": 10007,
"count": 1,
"total_bytes": 715,
"max_bytes": 715
}, {
"kind": 10086,
"count": 1,
"total_bytes": 715,
"max_bytes": 715
}, {
"kind": 22729,
"count": 1,
"total_bytes": 3030,
"max_bytes": 3030
}, {
"kind": 22540,
"count": 1,
"total_bytes": 426,
"max_bytes": 426
}, {
"kind": 22671,
"count": 1,
"total_bytes": 3194,
"max_bytes": 3194
}, {
"kind": 30618,
"count": 1,
"total_bytes": 460,
"max_bytes": 460
}, {
"kind": 23118,
"count": 1,
"total_bytes": 430,
"max_bytes": 430
}, {
"kind": 38421,
"count": 1,
"total_bytes": 1069,
"max_bytes": 1069
}, {
"kind": 10377,
"count": 1,
"total_bytes": 664,
"max_bytes": 664
}, {
"kind": 11111,
"count": 1,
"total_bytes": 382,
"max_bytes": 382
}, {
"kind": 34550,
"count": 1,
"total_bytes": 867,
"max_bytes": 867
}, {
"kind": 22307,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 1078,
"count": 1,
"total_bytes": 13969,
"max_bytes": 13969
}]
}
@@ -0,0 +1,938 @@
{
"duration_seconds": 376,
"total_events": 5992,
"total_bytes": 8743262,
"unique_pubkeys": 1142,
"relay_count": 1,
"relays": [{
"url": "wss://relay.primal.net",
"events": 5992,
"eose": 0,
"closed": 0,
"notice": 0,
"errors": 0,
"disconnects": 3,
"connected": true
}],
"kinds": [{
"kind": 30078,
"count": 495,
"total_bytes": 2024957,
"max_bytes": 54029
}, {
"kind": 21059,
"count": 386,
"total_bytes": 1099128,
"max_bytes": 5314
}, {
"kind": 4515,
"count": 374,
"total_bytes": 781718,
"max_bytes": 14221
}, {
"kind": 22591,
"count": 371,
"total_bytes": 286839,
"max_bytes": 1228
}, {
"kind": 20001,
"count": 271,
"total_bytes": 315643,
"max_bytes": 2029
}, {
"kind": 22734,
"count": 264,
"total_bytes": 112728,
"max_bytes": 427
}, {
"kind": 30382,
"count": 255,
"total_bytes": 121375,
"max_bytes": 483
}, {
"kind": 30383,
"count": 237,
"total_bytes": 126938,
"max_bytes": 547
}, {
"kind": 1,
"count": 212,
"total_bytes": 211399,
"max_bytes": 2011
}, {
"kind": 5,
"count": 183,
"total_bytes": 91915,
"max_bytes": 664
}, {
"kind": 22236,
"count": 146,
"total_bytes": 104252,
"max_bytes": 810
}, {
"kind": 0,
"count": 145,
"total_bytes": 103418,
"max_bytes": 2012
}, {
"kind": 7,
"count": 138,
"total_bytes": 79421,
"max_bytes": 1036
}, {
"kind": 22743,
"count": 134,
"total_bytes": 57352,
"max_bytes": 428
}, {
"kind": 1059,
"count": 132,
"total_bytes": 430222,
"max_bytes": 5285
}, {
"kind": 29333,
"count": 130,
"total_bytes": 63960,
"max_bytes": 492
}, {
"kind": 22611,
"count": 117,
"total_bytes": 50147,
"max_bytes": 429
}, {
"kind": 10002,
"count": 114,
"total_bytes": 510211,
"max_bytes": 42449
}, {
"kind": 23456,
"count": 72,
"total_bytes": 121616,
"max_bytes": 1874
}, {
"kind": 22850,
"count": 71,
"total_bytes": 30530,
"max_bytes": 430
}, {
"kind": 22684,
"count": 70,
"total_bytes": 30170,
"max_bytes": 431
}, {
"kind": 22838,
"count": 63,
"total_bytes": 27027,
"max_bytes": 429
}, {
"kind": 23003,
"count": 63,
"total_bytes": 26964,
"max_bytes": 428
}, {
"kind": 22774,
"count": 62,
"total_bytes": 26536,
"max_bytes": 428
}, {
"kind": 22876,
"count": 61,
"total_bytes": 28880,
"max_bytes": 3193
}, {
"kind": 22583,
"count": 61,
"total_bytes": 26047,
"max_bytes": 427
}, {
"kind": 22613,
"count": 60,
"total_bytes": 43172,
"max_bytes": 1133
}, {
"kind": 4,
"count": 58,
"total_bytes": 61778,
"max_bytes": 17018
}, {
"kind": 25555,
"count": 56,
"total_bytes": 249772,
"max_bytes": 16895
}, {
"kind": 25050,
"count": 51,
"total_bytes": 20451,
"max_bytes": 401
}, {
"kind": 11998,
"count": 48,
"total_bytes": 22272,
"max_bytes": 465
}, {
"kind": 22728,
"count": 48,
"total_bytes": 20592,
"max_bytes": 429
}, {
"kind": 31234,
"count": 33,
"total_bytes": 77859,
"max_bytes": 2612
}, {
"kind": 13194,
"count": 32,
"total_bytes": 16448,
"max_bytes": 514
}, {
"kind": 20387,
"count": 32,
"total_bytes": 16448,
"max_bytes": 514
}, {
"kind": 24133,
"count": 32,
"total_bytes": 23680,
"max_bytes": 2214
}, {
"kind": 3,
"count": 31,
"total_bytes": 167289,
"max_bytes": 61894
}, {
"kind": 4004,
"count": 26,
"total_bytes": 30394,
"max_bytes": 1169
}, {
"kind": 22816,
"count": 26,
"total_bytes": 11154,
"max_bytes": 429
}, {
"kind": 20004,
"count": 26,
"total_bytes": 13878,
"max_bytes": 549
}, {
"kind": 30315,
"count": 26,
"total_bytes": 17657,
"max_bytes": 872
}, {
"kind": 443,
"count": 25,
"total_bytes": 43504,
"max_bytes": 22480
}, {
"kind": 38501,
"count": 24,
"total_bytes": 17819,
"max_bytes": 744
}, {
"kind": 1985,
"count": 24,
"total_bytes": 11435,
"max_bytes": 483
}, {
"kind": 22882,
"count": 23,
"total_bytes": 20949,
"max_bytes": 1455
}, {
"kind": 5300,
"count": 22,
"total_bytes": 13434,
"max_bytes": 701
}, {
"kind": 31990,
"count": 22,
"total_bytes": 21777,
"max_bytes": 6861
}, {
"kind": 22790,
"count": 21,
"total_bytes": 8988,
"max_bytes": 428
}, {
"kind": 21050,
"count": 19,
"total_bytes": 17510,
"max_bytes": 1874
}, {
"kind": 10100,
"count": 19,
"total_bytes": 22108,
"max_bytes": 1589
}, {
"kind": 20333,
"count": 18,
"total_bytes": 14340,
"max_bytes": 798
}, {
"kind": 30088,
"count": 16,
"total_bytes": 29798,
"max_bytes": 1871
}, {
"kind": 6,
"count": 16,
"total_bytes": 28131,
"max_bytes": 3369
}, {
"kind": 30091,
"count": 16,
"total_bytes": 9842,
"max_bytes": 629
}, {
"kind": 6300,
"count": 15,
"total_bytes": 45643,
"max_bytes": 8157
}, {
"kind": 22658,
"count": 15,
"total_bytes": 11909,
"max_bytes": 1161
}, {
"kind": 30311,
"count": 15,
"total_bytes": 16556,
"max_bytes": 1197
}, {
"kind": 445,
"count": 15,
"total_bytes": 45070,
"max_bytes": 4130
}, {
"kind": 10777,
"count": 14,
"total_bytes": 6840,
"max_bytes": 489
}, {
"kind": 9735,
"count": 14,
"total_bytes": 27231,
"max_bytes": 2777
}, {
"kind": 4244,
"count": 12,
"total_bytes": 6768,
"max_bytes": 564
}, {
"kind": 22456,
"count": 12,
"total_bytes": 6808,
"max_bytes": 790
}, {
"kind": 38503,
"count": 11,
"total_bytes": 6070,
"max_bytes": 552
}, {
"kind": 10050,
"count": 11,
"total_bytes": 4882,
"max_bytes": 592
}, {
"kind": 30515,
"count": 10,
"total_bytes": 53040,
"max_bytes": 5304
}, {
"kind": 30512,
"count": 10,
"total_bytes": 15210,
"max_bytes": 1596
}, {
"kind": 30089,
"count": 10,
"total_bytes": 164434,
"max_bytes": 44372
}, {
"kind": 10000,
"count": 10,
"total_bytes": 16448,
"max_bytes": 4287
}, {
"kind": 22809,
"count": 9,
"total_bytes": 3852,
"max_bytes": 428
}, {
"kind": 22486,
"count": 9,
"total_bytes": 3834,
"max_bytes": 426
}, {
"kind": 22376,
"count": 8,
"total_bytes": 3550,
"max_bytes": 444
}, {
"kind": 7368,
"count": 8,
"total_bytes": 8294,
"max_bytes": 1040
}, {
"kind": 38500,
"count": 8,
"total_bytes": 5696,
"max_bytes": 713
}, {
"kind": 30380,
"count": 7,
"total_bytes": 3178,
"max_bytes": 454
}, {
"kind": 20000,
"count": 7,
"total_bytes": 2422,
"max_bytes": 346
}, {
"kind": 22104,
"count": 7,
"total_bytes": 3105,
"max_bytes": 444
}, {
"kind": 23025,
"count": 7,
"total_bytes": 3017,
"max_bytes": 431
}, {
"kind": 22896,
"count": 7,
"total_bytes": 3003,
"max_bytes": 429
}, {
"kind": 33334,
"count": 7,
"total_bytes": 5817,
"max_bytes": 831
}, {
"kind": 22835,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 30443,
"count": 6,
"total_bytes": 7297,
"max_bytes": 1260
}, {
"kind": 22633,
"count": 6,
"total_bytes": 6097,
"max_bytes": 1668
}, {
"kind": 22392,
"count": 6,
"total_bytes": 2662,
"max_bytes": 444
}, {
"kind": 1984,
"count": 6,
"total_bytes": 3180,
"max_bytes": 530
}, {
"kind": 30064,
"count": 6,
"total_bytes": 4438,
"max_bytes": 991
}, {
"kind": 22715,
"count": 6,
"total_bytes": 2562,
"max_bytes": 427
}, {
"kind": 30280,
"count": 6,
"total_bytes": 3078,
"max_bytes": 513
}, {
"kind": 22559,
"count": 6,
"total_bytes": 2574,
"max_bytes": 429
}, {
"kind": 22813,
"count": 6,
"total_bytes": 2562,
"max_bytes": 427
}, {
"kind": 22635,
"count": 6,
"total_bytes": 2556,
"max_bytes": 426
}, {
"kind": 22746,
"count": 5,
"total_bytes": 2155,
"max_bytes": 431
}, {
"kind": 22364,
"count": 5,
"total_bytes": 2135,
"max_bytes": 427
}, {
"kind": 37195,
"count": 5,
"total_bytes": 3664,
"max_bytes": 786
}, {
"kind": 22571,
"count": 5,
"total_bytes": 4055,
"max_bytes": 1127
}, {
"kind": 23004,
"count": 5,
"total_bytes": 2145,
"max_bytes": 429
}, {
"kind": 22834,
"count": 5,
"total_bytes": 4050,
"max_bytes": 1126
}, {
"kind": 22636,
"count": 5,
"total_bytes": 3280,
"max_bytes": 1164
}, {
"kind": 22786,
"count": 4,
"total_bytes": 1720,
"max_bytes": 430
}, {
"kind": 25195,
"count": 4,
"total_bytes": 2712,
"max_bytes": 678
}, {
"kind": 4243,
"count": 4,
"total_bytes": 2256,
"max_bytes": 564
}, {
"kind": 22168,
"count": 4,
"total_bytes": 1774,
"max_bytes": 444
}, {
"kind": 22760,
"count": 4,
"total_bytes": 2239,
"max_bytes": 695
}, {
"kind": 22733,
"count": 4,
"total_bytes": 1696,
"max_bytes": 424
}, {
"kind": 22878,
"count": 3,
"total_bytes": 4059,
"max_bytes": 3203
}, {
"kind": 22902,
"count": 3,
"total_bytes": 5023,
"max_bytes": 1675
}, {
"kind": 22428,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 25196,
"count": 3,
"total_bytes": 4938,
"max_bytes": 2214
}, {
"kind": 22325,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 4245,
"count": 3,
"total_bytes": 1332,
"max_bytes": 444
}, {
"kind": 22258,
"count": 3,
"total_bytes": 1331,
"max_bytes": 444
}, {
"kind": 22625,
"count": 3,
"total_bytes": 1284,
"max_bytes": 428
}, {
"kind": 22631,
"count": 3,
"total_bytes": 1281,
"max_bytes": 427
}, {
"kind": 22551,
"count": 3,
"total_bytes": 1816,
"max_bytes": 695
}, {
"kind": 22763,
"count": 3,
"total_bytes": 2988,
"max_bytes": 1331
}, {
"kind": 3927,
"count": 2,
"total_bytes": 1066,
"max_bytes": 533
}, {
"kind": 22186,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30213,
"count": 2,
"total_bytes": 1264,
"max_bytes": 632
}, {
"kind": 22367,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22783,
"count": 2,
"total_bytes": 1391,
"max_bytes": 697
}, {
"kind": 22616,
"count": 2,
"total_bytes": 858,
"max_bytes": 429
}, {
"kind": 22268,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22279,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22474,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22407,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22729,
"count": 2,
"total_bytes": 854,
"max_bytes": 427
}, {
"kind": 22234,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 10003,
"count": 2,
"total_bytes": 18782,
"max_bytes": 15076
}, {
"kind": 30815,
"count": 2,
"total_bytes": 75068,
"max_bytes": 37534
}, {
"kind": 1111,
"count": 2,
"total_bytes": 1775,
"max_bytes": 922
}, {
"kind": 30087,
"count": 2,
"total_bytes": 13286,
"max_bytes": 7325
}, {
"kind": 22256,
"count": 2,
"total_bytes": 886,
"max_bytes": 443
}, {
"kind": 22299,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30090,
"count": 2,
"total_bytes": 2911,
"max_bytes": 1515
}, {
"kind": 22260,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 7376,
"count": 2,
"total_bytes": 1340,
"max_bytes": 670
}, {
"kind": 22012,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 30618,
"count": 2,
"total_bytes": 920,
"max_bytes": 460
}, {
"kind": 1078,
"count": 2,
"total_bytes": 28397,
"max_bytes": 14219
}, {
"kind": 22313,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 31339,
"count": 2,
"total_bytes": 2940,
"max_bytes": 1470
}, {
"kind": 15750,
"count": 2,
"total_bytes": 1184,
"max_bytes": 620
}, {
"kind": 20,
"count": 2,
"total_bytes": 1664,
"max_bytes": 834
}, {
"kind": 22326,
"count": 2,
"total_bytes": 887,
"max_bytes": 444
}, {
"kind": 22841,
"count": 1,
"total_bytes": 428,
"max_bytes": 428
}, {
"kind": 22738,
"count": 1,
"total_bytes": 1668,
"max_bytes": 1668
}, {
"kind": 22295,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22296,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 31510,
"count": 1,
"total_bytes": 979,
"max_bytes": 979
}, {
"kind": 22691,
"count": 1,
"total_bytes": 431,
"max_bytes": 431
}, {
"kind": 22340,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30003,
"count": 1,
"total_bytes": 387,
"max_bytes": 387
}, {
"kind": 22197,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22257,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30079,
"count": 1,
"total_bytes": 875,
"max_bytes": 875
}, {
"kind": 22811,
"count": 1,
"total_bytes": 3211,
"max_bytes": 3211
}, {
"kind": 38385,
"count": 1,
"total_bytes": 1350,
"max_bytes": 1350
}, {
"kind": 22339,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 13196,
"count": 1,
"total_bytes": 656,
"max_bytes": 656
}, {
"kind": 20200,
"count": 1,
"total_bytes": 491,
"max_bytes": 491
}, {
"kind": 20201,
"count": 1,
"total_bytes": 491,
"max_bytes": 491
}, {
"kind": 22395,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 30023,
"count": 1,
"total_bytes": 27062,
"max_bytes": 27062
}, {
"kind": 7403,
"count": 1,
"total_bytes": 490,
"max_bytes": 490
}, {
"kind": 22618,
"count": 1,
"total_bytes": 1668,
"max_bytes": 1668
}, {
"kind": 17,
"count": 1,
"total_bytes": 593,
"max_bytes": 593
}, {
"kind": 30000,
"count": 1,
"total_bytes": 823,
"max_bytes": 823
}, {
"kind": 22558,
"count": 1,
"total_bytes": 428,
"max_bytes": 428
}, {
"kind": 22143,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22198,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 7375,
"count": 1,
"total_bytes": 2846,
"max_bytes": 2846
}, {
"kind": 22506,
"count": 1,
"total_bytes": 426,
"max_bytes": 426
}, {
"kind": 22243,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 23086,
"count": 1,
"total_bytes": 431,
"max_bytes": 431
}, {
"kind": 22688,
"count": 1,
"total_bytes": 3201,
"max_bytes": 3201
}, {
"kind": 22975,
"count": 1,
"total_bytes": 429,
"max_bytes": 429
}, {
"kind": 22270,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22584,
"count": 1,
"total_bytes": 1675,
"max_bytes": 1675
}, {
"kind": 22764,
"count": 1,
"total_bytes": 551,
"max_bytes": 551
}, {
"kind": 23071,
"count": 1,
"total_bytes": 1675,
"max_bytes": 1675
}, {
"kind": 22817,
"count": 1,
"total_bytes": 559,
"max_bytes": 559
}, {
"kind": 22614,
"count": 1,
"total_bytes": 3200,
"max_bytes": 3200
}, {
"kind": 22330,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 22229,
"count": 1,
"total_bytes": 443,
"max_bytes": 443
}, {
"kind": 24421,
"count": 1,
"total_bytes": 525,
"max_bytes": 525
}]
}
+63
View File
@@ -0,0 +1,63 @@
import json, sys, os
from collections import Counter
def analyze(filepath):
kinds = Counter()
sizes = Counter()
pubkeys = Counter()
total = 0
total_bytes = 0
max_size = 0
max_size_kind = None
relay_msgs = []
with open(filepath) as f:
for line in f:
line = line.strip()
if not line:
continue
try:
ev = json.loads(line)
except:
relay_msgs.append(line)
continue
kind = ev.get('kind', -1)
kinds[kind] += 1
total += 1
pk = ev.get('pubkey', '')
pubkeys[pk] += 1
sz = len(line)
sizes[kind] += sz
total_bytes += sz
if sz > max_size:
max_size = sz
max_size_kind = kind
print(f"{'='*60}")
print(f"File: {os.path.basename(filepath)}")
print(f"{'='*60}")
print(f"Total events: {total}")
print(f"Total bytes: {total_bytes:,}")
print(f"Avg size: {total_bytes/max(total,1):,.0f} bytes")
print(f"Max event: {max_size:,} bytes (kind {max_size_kind})")
print(f"Unique pubkeys: {len(pubkeys)}")
print(f"Relay messages: {len(relay_msgs)}")
if relay_msgs:
print(f" First few: {relay_msgs[:3]}")
print()
print(f"{'Kind':>8} {'Count':>8} {'%':>6} {'Total Bytes':>14} {'Avg Bytes':>10}")
print(f"{'-'*8} {'-'*8} {'-'*6} {'-'*14} {'-'*10}")
for kind, count in kinds.most_common():
pct = count / total * 100
tb = sizes[kind]
avg = tb / count
print(f"{kind:>8} {count:>8} {pct:>5.1f}% {tb:>12,} {avg:>8,.0f}")
print()
if __name__ == '__main__':
for f in sys.argv[1:]:
analyze(f)
View File
+2
View File
@@ -0,0 +1,2 @@
connecting to relay.damus.io... status 503
failed to connect to any of the given relays.
+1
View File
@@ -0,0 +1 @@
connecting to nos.lol... ok.
@@ -0,0 +1,100 @@
{"kind":1059,"id":"3b112c9920bc79584669face207b9491a2a2da388110b84aa085a543d99ac841","pubkey":"c91a8fba68b9d08b9917e3a7443083a9b1364c984685bc56e2310024507612e9","created_at":1785677036,"tags":[["p","0ac458d69b9340c2e8a20a4d5f3afac1211b59e372a646beec37ac32b05ea4cb"]],"content":"v2:v4hmDySAiOH_Iv0uoZ0c5ctYsNwIFjWYQaAr5RPg5s-jqC95LxG56m_gZyio5uKm_7bImZ7hKuyoKXPx8_lfvJsg042FpZnUE8kCU8gAbl0lPTlHbwi-uPL43u0p1SACh1HHf8zyQMLaSBEIV-FTIghxtz9nIjo962bVbCqbh1sk_iMJ6qeEkmmYOCukb8ryRBYnW_7RsruPUsXnbGq5-JS4LXaKwmFmk78GYnaVaRzr_9IlQfUNKe2KD89hcsg5-SbOfLX-YEXeIeB7fKGT741tCtHs9JOBvwA7pgGwdfvUHUmz7WbSv4E6au2JENZ944rkxkv-8LinUAt0vlkXvzmHh-vwhnF9d09BZSj_tHvX2d7PEVg70EgifE6p6X5NnOjILq7VaSYTf2BLfkGl_dwV0R3pGL5_EtHr4abVGCeWTn30YJdUYHu7WV_7YuaX8tAxi0QtZ_5wstZEi8EkIS7R-ocJHNLVX8V3qp8hbFJ9jw_ZZv2Yjfjs_1CZLY7z-1u1WVjcBeXzEGQj5jnQTsf05D9Lv0fE9dmz5UL172LIJhDRQarhtowUJihycoQvRgcY8QH9wZzND-xCclxoncgku2x86TPyMmOPE9vhZX-UeimbJVSFO_PUt71quXF3l6kSLJqGK9Kpjf_yTKlcOiWqRlZ89QYuPfX5jNWEvWfv-g2rLmq2HM59Yq_J7iZZmXB_j0MH0Wz95Vdy6BdFkKA2boYWh7EKTslQHNw7uFZAsbH4keW64urcx1f21y6ccssz3snUGesmxlflSgbQCaY0C4J91OSOU8HsjGwbF1JIAK-UmLwNXsr645Z8iVingbQnSiUyR_RCWgufGaT03bJVp0uZNhrEAHckvYQ15mhHLDSIJoNCiTr0nBfpc1_iupYVQYXjWLgDcFIXhYbrr_cGPL_7sCVrfxKzrCbcm4Nz2LQo2ICrKWFUvWsDOw7Rp8e-gjLaLQxf349rgPPH0bz3VpA_vyiina8G8MGjZ1DnmRRyWn7IqN1Zc2x81_eYu4d25QAeqtctF86keK-nwQcQJt-frFmn9_Ei4uaj56g7YPTIFJF7VLV1af5xf8QfnsDnstmXejfIfq59k6H8-jJKrYbQ3Yue58qTin0wgUOKkPUmq0hml7BeQCykConuV33wV8Y0pJcPkTo69ufeaB8q8t-6xx78-mYNJWLM1KjXLLwqoG6Qqs0-CaASLVYJL7oVFZBpzD1FM8M4h_1d4YC9-_g22ilzm2wu2Z_EHaTOn184oiIpjg80umDtGoNgDejcAb9R8gdnI5SbLo-ianC52UO4piD1DheYurE0sDIuTbbnGgT03q4xtu3xxi-olmPScr3CHvH3mt8wWgjwRGIUgH71yoT0dvPUq6APmqOz_FAMLhFws8rMFaYLzoyzO3knSdwVkNyBqrzapRtP4DiD2pzx8w7mHI6bEpc7zMJZquJ6ksM8g9k","sig":"909df887f9f6ff688a919d16cf49a9cfc542c70e57676a663baa020aa10a28ae01d39aadf79b9ead4ef0f9a95e6af8205ca371b928f2dc498ff5f3b184d75e97"}
{"kind":38501,"id":"73b2e63fc89ae708f89d2d355cffebcdd5bbfcea39da46e6410b408f8b9fe2b9","pubkey":"45ac97d8c6b2896d43dbc903105ad722548ff1b986269654ebdc0228ffed4bb3","created_at":1785676888,"tags":[["d","btf-descriptor-2"]],"content":"{\"created\":1785676888,\"enc\":\"3d7e99d510750b3af7b20e45c2116c88f2fca84c5147f3c839a83b4be80a982f\",\"meeting_node\":\"92.246.128.180:8434\",\"pubkey\":\"45ac97d8c6b2896d43dbc903105ad722548ff1b986269654ebdc0228ffed4bb3\",\"sig\":\"a32af8b0a024dc7a67fa881041ccd4f1c50bcd600d7e8bdac5aadc143a3c2fbeca6ba616ded91df6e92d3669cc8f8114a15778786293e1822f8b6f99867efb55\",\"v\":2}","sig":"b00356fe4f3371d47d3348ca1d5d8d66b92da75ee8f3fe7799fb5744127fa4a76758a97db7bee777b30d4da3e710d6497009e6f9775275ac516f3dbe3cd52830"}
{"kind":30078,"id":"769fffcd14f4044105b49e1233169861c48eee0f156e70a0d3c5bbe4dce4caee","pubkey":"45ac97d8c6b2896d43dbc903105ad722548ff1b986269654ebdc0228ffed4bb3","created_at":1785676888,"tags":[["d","bitflash-mainnet-2"],["t","bitflash-mainnet-2"]],"content":"82.67.0.226:8433","sig":"60557df1a6c814e0f815d5cd4d5e8285e7e5a724080af10b5ab0ae382e70a42e1e6af0a9ecf62cbba4e0ba8ff60ef16f485be5ffdc2ee809b7769799a0465a11"}
{"kind":445,"id":"1cb1e652eee8d63d61792139730edae9141329d91c37afb61b6cd5cc3c2cfa27","pubkey":"6bf7822615d8091f8d5b76879cf8e8bef5f20b2a73d81e36bc06bb5b4d5e3034","created_at":1785676878,"tags":[["h","8c3f1c25eefc49fb83a272a826d5a36235381ffdd1ba0c7fa4d774f0c7f1b55b"],["encoding","base64"]],"content":"Ygn9YOUc48RIHWWOgepFIxy0yRbxXB1LWu3Md/RE8sbwnnF50ijaMTQTiVCxlb5s6dITCwpFaz4oPOHIwre/vn4Se9+WXFA2sgl9iNAanzh4ltnzP1c9mG6zQtZRRSYyvCBtBrsAx333eiOKMzSPFov9QqCK0F9Pwz+5Ee3CQpVC/hJfmo0vz4RHOe6WvxW4tjPbGFVd5PrMqeGv8xtMKlAYDdKZIXmRPySxllV7+zH17sN56eRITT/6zRq0enPORGqIAsgQqIZOSe652mRyOqkvkaJ9sxv3IyEb/u8gnZ9sdOuSQs1iRAGQgvLoCKBoTqoIAqMZT6YL/NjhuQ0NzZVZLXJwggfM7rBlv3F0Ad0MgRO6HMpSXxyxKSW1T0/+rgxcvNV7Q3DNOEMOPugCyfmrKboLIHfBSTyFgKdrQ6gZZtwUA72TgGLQctXjCzXwudG+n/q2s1KAItcmG6BpxiqQYAi7iw+fOivUwz41ra8x0hpZEVbU6K9wHwpjhN9tcqq+j6ALdjkFdnffRCMUMXB5GjLubaTdA4MCWZvEGdM5YACnmcCNhTMJMvLzurm3d1+8V/wwQvsIiYIYoa6Ph/ZoXvvE6P/Pvmrg+mARugiS3rSPyE7wZDcTXrhC/gGzBJdLrTW1hDgse5QFh+/m21O0eWJFweOnZSBWYkgDotuyAhzInsHZ4Lh3KtCMnAV/O12j5R78VtwwZHrT4nnejtenNxzIcwmSNpn10kTalHPYWOw=","sig":"a7d756444139f09ebb1c69c38cbd76787d6a2d9d30a2262853f017c78420177fc40307d803b7fe4f472332cfa312f0e0549da0d258648ea58c3792905a2ea05c"}
{"kind":7,"id":"7db435801117613a1eeffa3eaf68a9fc51fbade2951f16b7ddfa0b44ea367113","pubkey":"21c1a52506a343d12c34c0a434f7c6cda276a609ee51a0a02533febf0c84fb3a","created_at":1785676832,"tags":[["e","0000352da8dcd26bb6cbbada87e4ad466090ee86caa2e1a039eb7ee7d9b5a415"],["p","40b9c85fffeafc1cadf8c30a4e5c88660ff6e4971a0dc723d5ab674b5e61b451"],["k","1"]],"content":"😂","sig":"865e4ddde04a31a03dd07c7f3db1f87a87dee1057d60497b9f234b1f5d4d2919fe2db33b8810b8a0f557e5fb7bcde1fd2cb3a87336857f7e24333250b3a674a6"}
{"kind":22749,"id":"e1abe91e8827ce0b42b55440d14182531ec10413503e0f0df44bf6ffb1d2ce77","pubkey":"38879d5f7d6ca197f5a43f4043cde208002391eda3d7a66529cc29f95754fb5f","created_at":1785676832,"tags":[["x","14305u172l2k3a2wn1e103y5j6k5v4q69c214a"]],"content":"{\"peerId\":\"hMQ2j8o62EZB1aaiWa0D\"}","sig":"d2a05f9d679f36d1ad95b172ec7343e42f26225621e2a05e8ae79cdd779bec6b0800a74165c66bfc6b3af283a1e1f833890ac87301b15188cd7d6775f86e5805"}
{"kind":21059,"id":"ffc75a95b53a380a2de1114ecfa1426c080d5908df48fc66ce209c429ec8d4d3","pubkey":"a249d052bdf16251537b47555586389c8090db4a08687811edfb8a5dbe574044","created_at":1785676831,"tags":[["p","32a6df90883d1d026ce75ca91bfed54353fd2feb9e9f7329f7a94cb467037b05"],["expiration","1785676951"]],"content":"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","sig":"8a6d018e1563b415ed70a13ebd5adf1fe2ee36f92006c570bcfffa198646314280f07fa6788801ce809fcf8e8b5470f629f836076ba19d30600eb273ffbfc542"}
{"kind":21059,"id":"518b7655489f9339f73a7d4cc9013b817e731288dea4b45d2e5eb6485f438489","pubkey":"7b353d5d4669607809fdc4e22690fe19e068912e0aa67c5d83c65ed12803e7e7","created_at":1785676831,"tags":[["p","32a6df90883d1d026ce75ca91bfed54353fd2feb9e9f7329f7a94cb467037b05"],["expiration","1785676935"]],"content":"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","sig":"c7c4162fc010944e98390ba22f03d2027dc635c2ec5bc52501d43cec6bc2bd78bd515de180b5586d961d9dc8b374e29597b548c28376b018b8264e28c3860f6a"}
{"kind":21059,"id":"5e8ffd1a6c5f869a3a04aef3bc0cb701930c4348110ba8d52ff0549c0f463d58","pubkey":"79e0b421158103026cfd58b00ed88d52fea107e5974f419879d80a5b7ce3d40a","created_at":1785676831,"tags":[["p","32a6df90883d1d026ce75ca91bfed54353fd2feb9e9f7329f7a94cb467037b05"],["expiration","1785676951"]],"content":"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","sig":"bc53226df7c6b32d70168c939b2f810791493f408f365d779e652ab9cf9a88203b25b8d2f40d12ffa29e90101528de9078be4a146317b1ea1ccd80b56af6022a"}
{"kind":38501,"id":"1a8b39de152c04c4cf7d46e9ecd4b05d718cc83f8c283e89c920b5ef91a72a81","pubkey":"bb4f1716ebb55c0cc2759c6ec70be71884486568237be5b0a24fb94b87ab0d5a","created_at":1785676831,"tags":[["d","btf-descriptor-2"]],"content":"{\"created\":1785676831,\"enc\":\"d395724b1f1d6cec4f76cb341404015ace916a410ce93993358b6ee568051329\",\"meeting_node\":\"92.246.128.180:8434\",\"pubkey\":\"bb4f1716ebb55c0cc2759c6ec70be71884486568237be5b0a24fb94b87ab0d5a\",\"sig\":\"56006b2ad9357724b0848a25f049343e59177e414550d609a1970900b80fe679335e7fbf09503d5581513676daed140d68313c6d6f0efd075e9b2cd4ca52d8cb\",\"v\":2}","sig":"111a08e451e1733ecbf6fa8e89ad9bff22f9e49bb5059688cdb741db58851656143013b8ee33fe0668e13ff5bc2348764631bad217da59d26ab82775002ab9e5"}
{"kind":21059,"id":"1fa1cc7fb9b195dfb7bb587649e1f57a13c22a716d5b6b7ad1eaa35f3682ba36","pubkey":"df37a8e8a754e2aa7c61085a67f70c11c548381b45f6bfa45c1fe4fc6a05d60f","created_at":1785676831,"tags":[["p","9131564f29a6caa4884f5dc2bc7f2f37cf9f9f3ea79eac6cbee56f23faacfdc4"],["expiration","1785676951"]],"content":"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","sig":"b4f89cad58ddb2840fce5d18b59703412e7781d721482486e53c10a540ca6bb91266bd747ee03301cc4bc638838c2f8e1d19aeb7959597ad3a9c667c808e52a1"}
{"kind":21059,"id":"c6a07a7a6c5efe9492211f5c7ac91af555b672564450a29a13d6c4d480da58e5","pubkey":"33f9b8d989cc0db6caa5503b2e59a174550054c8eb93688641d71371ae305338","created_at":1785676831,"tags":[["p","605e6516fc17fab93c5e1c66534b5d4a8a2b3de64942c3ed742b51626d267cf5"],["expiration","1785676945"]],"content":"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","sig":"291c834d1ba72b4cc0830e8f69bc4a07cee371fdfdad2466d1cfc6cc69b4866cf5e3895ebdfdc43255320a9473423f83f1a2c92d37d7e9acf51e46e344a6e814"}
{"kind":21059,"id":"0f9efae4bd3dc697c547733b74c1c0b2f36c17a14031ad3f9257e35ce8c11d18","pubkey":"a6b8b3720e7a311904a9d6d5ddfcacedd7e0a70654bd41c043072de6f53f8107","created_at":1785676831,"tags":[["p","4f8e06e55adc2dff717adbde066c79a192468858816257c8c717dcaa8d51a28e"],["expiration","1785676951"]],"content":"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","sig":"70c6036ef7b66b2954d3ac4a944f8c5234bad1b37b43a50cfd947fe8e984e0cbf91b3ac51b17aa8babcfadfbd27affe5dee578c36103a9660290e7aabcc814b8"}
{"kind":21059,"id":"233787bfae7c7d89e6d09ee4d1348e59be138204edec57ab399abed267e9ad43","pubkey":"38cd9712528928a6b85f526abf6613917c52c636608e1f7411b33ee457d5bc4b","created_at":1785676831,"tags":[["p","7b938b906ad984ea438f8af5e464ae3c2fcca4acefcfadbe7a3d66a7a69212c0"],["expiration","1785676951"]],"content":"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","sig":"e7894c60e30fdbcb92032f6e2a80090b2474996796771becec02a2e1af748f3c893b113cc6ab197b6fadeceb10805fcdba74ec43617cb665199ebad82209a2d4"}
{"kind":21059,"id":"f22714bcb70a4b41ffd4712f8e5764a0d6bd3af78f6107134932bf8531f52621","pubkey":"fbae19b87545bf7615aeb74ac04cf926a50bf332f3b8c2a0aa5f4c877d36e351","created_at":1785676831,"tags":[["p","fc62a3263abf4a39c09a7b7064aa40480788f868b653c66dc6e3a32c6880a506"],["expiration","1785676951"]],"content":"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","sig":"69949c1cb10548c9a991212c50390aff75777fa2eaf5f979c23f7e031d48fbd0202b7882248552330464dccd8fdd089b613e46e343e540ea49c111005da39d01"}
{"kind":21059,"id":"83cd0f7e4b2e434807599c79a7f0549266bd37f0b91847b0c1cc9681417c9041","pubkey":"b6b05517cc607db1425e192c3ced2df864b46000526d5f50e47b74d25c99436a","created_at":1785676831,"tags":[["p","08e99116ad58c5aa5e6e7dc40668d5d7f2c080fc9fd697ec0aeb76880cf54bd9"],["expiration","1785676951"]],"content":"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","sig":"466fcb9413f41abf5337adc2032829603aed33963cdaa304cdf5522bd45b59905298465515310b00588aea216c0aa3249618d60e907a19363bf84bddac67cd25"}
{"kind":21059,"id":"0afb26e3a41a100c304aedc5b80548e679da585e4af81391b7e0196f5eb694ee","pubkey":"d66b7344ecf950b1261dd49de99deea8b325f038dcc1ed9e436b57f9fccefbe3","created_at":1785676831,"tags":[["p","fde8ca9b4a9d1f5d2ed8b82ea13766c37bdbf6cb50bd272d642eaeacc713563d"],["expiration","1785676951"]],"content":"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","sig":"6e8c0633ec51e9d5663a865754116c27582180150f5eb7581570cef47b58449b79315c1623c2684eb4075f46120cdcdecf7fcdd59e57181132e754be7fd8d4de"}
{"kind":21059,"id":"eea2139089ca939ab3e858c7c626609fe629f4dec943497e0aa8bc5c49d13b78","pubkey":"71654ee31d04384b529f095a9cca21b58530ab86c35e239257ae5a6b86b1f70c","created_at":1785676831,"tags":[["p","5376ff74b7bb5a5c9e97cb60a1d068dbdbc5ec39c4346156335747b776ec6d84"],["expiration","1785676951"]],"content":"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","sig":"e03f0a94db48468a6a07bd0ae0a9792c4752f3d498497bbbb3b6fb1873bcea3ba8f2d08efd7e2ca67928a5183f3bf9361699785adbad273a1da60c58cbbd31d7"}
{"kind":10002,"id":"0ff496f20a3bc170afc15a4141498259c26fb487702ba35b9260a96b7b5ba0bb","pubkey":"0000521bbc284afcbc646771c0fda5b1a839a0d3cd7b6f4fe2eda9be15ecbc6b","created_at":1785676831,"tags":[["r","wss://relay.damus.io"],["r","wss://relay.mostro.network"],["r","wss://nos.lol"]],"content":"","sig":"111925db0e99748aedfae54d2442ea8053130095695c5cecf798f3d34cd4039afc4b41662ee128fde49e3c3d517523fe8a828b21d31033c7f4d264e43c2ed35a"}
{"kind":21059,"id":"7f897e9c805c499c963dd8999caf3645b5c6aa968568d5f37462ab8659e41853","pubkey":"6e7e9c51e2739dafb6348212fbf48fb6d768002b018125c8cfbbadfe70b37dd6","created_at":1785676831,"tags":[["p","aaf802c63614f6028fca31eedc3d0021bc3320fc95d0e3af5ddf8e7cae871472"],["expiration","1785676951"]],"content":"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","sig":"24b691e5868fdfa5546625783063d68c5d6bd412e389851e5555bb15b4c6aff3251474fa3580501b8cce93e9711a982dc2f1e7da6ae167ddf459ffec1af0d94f"}
{"kind":21059,"id":"ee1cdd05723f5a6ef4ad3caf9db988f33f9a2921a82cce6f7a24158218ab38ec","pubkey":"755a026b88668c9ce69857b49983a95039dd7689739acabc169a21315ee04783","created_at":1785676831,"tags":[["p","e15d56d1b0ca49d50a4f71cbf04774ae606c4632ad7752195ff9b80c5574e0b3"],["expiration","1785676951"]],"content":"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","sig":"0f74f06f1c14373856bac66f45fbef85bb774aa1e6a7ef82ee821c5dd81b3d32a6666b815d2b53613fb28d7eb63b7a6859c15dcc9ee6b67930ecfa086b10ded8"}
{"kind":22653,"id":"35b97981fe7f50b3f20cae1277b4861b9fa3d49ee7dc7acb8da9d45c1287399e","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676831,"tags":[["x","1w3v3i5c503a1e531y6b136q2s5466t39322o4"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\",\"offer\":{\"type\":\"offer\",\"sdp\":\"180,27,136,119,41,56,194,227,43,3,188,67,19,22,123,115$ZeuNQlPcrzNPMpEycw20kbmWmAQ9LQA2e4EBLwCM4jl3ajWwnnK7utcxXmapzWDz2xsFhpNRr1jg81viwHLkwzGfOHC4/0o7KKF5Jl4TnLLwSj8g6oToW1FNupDZeu9NkaY0j8JEJCU/G9iTJ/mnnTFSyMZUY9Y3AB5v9+uk+uBij+wmOk1Q4CBDPScFZll2oBNoq/GhlUP9BqhSavsUerOnATJMjqzcMT1m8pZYe8RV5KnCDn6YnSaIlje9LmePx1qMBQBWugymQGuaX5OufdXkvabgjXBMkoj2X1/I9KbAsNGWU2ePqgfGhYXO2IrpEuqwsyiYn9UaZN+RI5v6c847DIpLpVo12xlDg+gog8L1ueTawBSBLazJmCytxqvLROFKCv0EctnbbMKpGu79nsa8NPYcp6c5BVaYFZgWbDxHaEJx2vz85n4STQ8OhjyvA003IwSrE60SUu78URUKA9SXTQoQMbu7H9Uc3+JukT4W3wCMOiMgnJNqQYvYgr2+HJ5EFWLEyD8dlnmI838FJGwnHl05fkAv5vHmdT1Eyrbbstlrn9wrSEmla5mq3cf4lLSkb+06a4D47/j3QBrtfREhnc31O/+XCzhGYZKs5HajWQF3YVW3Md9nHCGFe+yYplq5pCe6HlVSbooXQsYuYyHH8BmfdZCA+Jkrhfbasr+MKTXvvR1ARfXuddxvOjuxk9uQbIN+k1eJmnlyzxODDhMi7gJmQeOpudYWlcquuPhSYHBzKlPs15ibSr/+/Ki/upX0+AZCywGZEyPQE5MYYD6jGoX1SyoZ4jIfl8Xw6oTOTJ+esJc9Ub5LQNBNLUhU8Lh5lxw4LPkZpnepAfyGno1BbrN6V5gg3LA+aoD8pQY8DajozXQX3OWTCu8CGZTu/RXK/VCEnUykrfYaXBU+88+F+3xAxS38G1jdek7VmAMqlfxGY/21roM4zVKahtTJTSZBDK6RtK60tZffGkxOugNHGTY6+DbOAa+NfLg2GWsSkcXSStUmCzFQbql5p4g8FiiO/3udMNlqIx1nxX0DWclYGgHi4DGJw7E/wyuXn7324Tvw/UjVQvm5fHVLc7LnClr//dsQArzagNjdIolCSC+pnI/EUkWAFVwcnwu5RpcPam6c6ZtLkXPhB0bK15vdRY4QUKLNSm0kGDDs3HC6/xi6oZQgF6j49ziOGeV5YYS42XVk41mZG4cGqX9R1EWGd4ZQFiWyRXNLttaMol2CJTQ+yJC1DYjD2N793qoMRuzLVXStld6436buBHJrqL4Wzywzi6eS6ANXyX4jqKxQlXOl9nTxCQ==\"}}","sig":"849aeadaa749a4d431cb91f8a282ac09b9fb322872ff0895430f978ee69108deb02f0cbd323a4b0a92148d4014d8bc8c503e22f9b05569d7d751824737661f9a"}
{"kind":22842,"id":"a497bc4e9330b78979645581cb7422c54b64d990b295d18fb965c15c8b28e0e4","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676831,"tags":[["x","3b47662o2j5h6w4431116d4l6k6z6z2o3s2tk51"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\",\"offer\":{\"type\":\"offer\",\"sdp\":\"167,11,114,204,116,88,224,166,141,164,164,64,134,197,97,18$zMoeDUlzCvWcmm9Mn8HB1tuqvdDd4BvM3uJBCzwaUKQ0gnUWY+HmKkpaABQex/UCuKjf0fdsRr7SHE3MQ2j4vbSabKo+LD9mdyeQt1vDGpiu1uWbq/Em0HVEEWxTTTAuySuo/cV34xa4On1Ju8LjUaaF2+Bf5udktCf+/EzzVZmGWlyWqbMmw1mg1D3UcSsye2IBiD0FpjDzRiqsweHRoqfESL6XuL5XZ1ka2oipiN4t3DtkIf+QncvSElMm4lxyOnqDR2HDcuQuJKsi9NtuXbZoyJ+rgdfbHAmKsT1hrywjk08aBI56JBUQvVv1ClKq5gRplDRscxlj4CYeksMayW8cHjxqchUEtPdqzOvvjbS/fS1hzEwplaQbYqkEINUSxCx0llT5023uwSMcZXFuPPzEpuu9L91zI1EwBYIjpIeUti2bDG+RtseCcdp8TQh6Ygwqc8Ne5HDzYQHrWqEQRSuurZp/w7+tES8NlAAmPSCPoPLyyKG57uvaWKysmRr3bL1jPdVS+Wqo0WbUMvrS6xEwYle1qvnK/6bP3JeaMhnriq6xTJiMDBplL21vOC7qwvA+sQgu/R/w7q3qcYFre9HbXipFTWY4x8QOGv/JMAf9YSSHmDYUsIKt0Rh3Ow6AJhJjC4bjem6QKpuznLwwRlWcv0sJaF0LtibH/z2bwKs6Q4OozajwS3aJ9YupJT1NcNBOdf0+9R3zZEENOghm8mbLIN0YIkSZXepR/gzn8D7fAt3Bh7gihZvZHReMytPWL3Hjoev30HO8ehBLGbJrpe+gkoxsnbU/zQT40ucy3Vp1KlbOCd7aGGSKqWvlFxbz+EraunwiLtp6gqML257CdGa03iZFKu9joOS98uSzUf3CRn9A28cbgHPvLTQ4UYj00TBb8v04uW6Kv1s0VCm0kZDt1V6moMlFGXu592B6I0aDBkWEr6/uKX1UymIsRBm3Wj5oWXVf8VEvDOYExMFJtHmUJg/z3/KUJASjoX+LAENQO8k28vlWh8b41YTVkA3sB06SZIRGoCVXcEs6+IJgqvuLv58OvZrfK20gSdGEeYdEWwy+w+4zJeaL3OZNbDJZIHCBRRvruSqiXG0OdKT2QnknrJ9otReAc/mN8E4wPwciDUsKV4278gWSZrFWuIPERcf7xxwmbxc/gXCE2bf84V+khN2YH15WtGBDErRNAbVfEwQdrAYzyGKiy3CaoxIIbOn/wR6WPFnImdzMlT2zcIlhLgTHCGo+5186vOk1M/AWsUyah36Pf0eQW3aCtb/nweENd/aGWqSaFzX+L4BHYuKSsT7stFc=\"}}","sig":"5358909a435dd89d34a1e61e94e5294945eb1eb021242dac22067aac7f0652228d9b4a7793a2b9956f879d41cbe70793eb2ff79088470ee3b6af3b01e99d136b"}
{"kind":5,"id":"6e783cf2000cb5793a94ae2b09162e1d3f1304be6eb7c774b852e66b59fd521d","pubkey":"1ec71c39a30373cbb10e91d6efa6ef1e9dad0756d610c1a4e337df016eeaaff8","created_at":1785676831,"tags":[["e","1f4d0d3d55c0fd387950ea4e0de7e973457039bfb03b824cc71b13bbcd39f293"],["e","3aed713425ab4a0459176d4ba589876e6a313d879fad18152a11347c22c1114a"]],"content":"","sig":"8bbc6c7ee40ccec7f402c48a5230f71bad3240599ab841e46a3e155841953edc52de47f96fd5b4dd35f8f784e1cc4fc9062303395d6d049d2c78744bb5c176ef"}
{"kind":5,"id":"df31df8ea137659a43641ec7aa14f85f6935907ae71e0ac42d5196f1c7f3022a","pubkey":"a6cd35843a1471debb1b174b5bab472d9f5a2e2391e92207c2279468c3e9ac9d","created_at":1785676831,"tags":[["e","3aed713425ab4a0459176d4ba589876e6a313d879fad18152a11347c22c1114a"]],"content":"","sig":"fcbe7b0f5cf5fe044cd295ce6ae5970ca9bcea40e0584b212c027426a1721c3f15c60e387b6f390c7568e7c68e6153512302fc3898a27b0094c2062409788dbb"}
{"kind":21059,"id":"bb18168cfcd10c7cfec63ae32e39d5c01ff248531c05ed8d42851751c56a8c85","pubkey":"99f289641b017b67d71e948bad494b63bcd016724e95e425bba176bc8cc03cd3","created_at":1785676831,"tags":[["p","29d1d9e11edbdbf04538d90d053b7b2d32c1c253fc3df1893c11b68826630609"],["expiration","1785676951"]],"content":"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","sig":"3504f1566dc03ab9fc19e597da5baf4a3734fdcb2f7a24316d0a5f43c37bfcd54e6f9bd1d4a0d8960d1ab5d38d77d7078ee990a2c874db0960300731266e1937"}
{"kind":31991,"id":"f8a2a8a05db114a7096435a285c45498846a0306d9538e7362b9c9665032b6db","pubkey":"537f2bbfdce17c83ee7338dc26229edd9b26226a2f258b578f91f4213611afc3","created_at":1785676831,"tags":[["d","537f2bbf-v1"],["s","available"],["L","agent-reach"],["l","heartbeat","agent-reach"]],"content":"{\"status\":\"available\"}","sig":"09a021823e02d5c2ae3ddda7d05c22e05033012645617cdd6313b5b425b491cc32c61d849d704c7cd9ff294c757d8e0ff26d553f457cd3d7e26c158a2651552c"}
{"kind":22543,"id":"75e596503238aa0f8c242cdb2011667c813515e0f02b006c34026105f67dc2c6","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676831,"tags":[["x","456a1r2o456h222f3g036k594s5x2333l2v45"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\",\"offer\":{\"type\":\"offer\",\"sdp\":\"36,99,139,51,68,73,226,255,159,72,228,209,82,242,77,19$m/DD2pfpEe+VV4XvRyouNbCVzG2hxkrSt+3zBRI1H8ihRyklrKo5mkvQvvG97YeFy79GxjZhneAlrf1ZzwSoxO/KsE7AtZiuYI/onBlne1ueaqNZca0ZLs/eVvLA3nksV0gf4IMc8g7zJ/IXHGK+9Ha2cSW81dPPlnIsL/7DPyzvtOTpTqENqXYwkpOe5yYa/lOhEF7XNWHDcJCiDonINlMc9JFvXqd+rXlBmrXMsI2De1l4EPGHAk0hnY04uLbpE9j2AFHNMU7JaLLfYyAB2hL4GEjxCqGXuCETm+oJ6hGfsmMeF1dHFfVj+JqsVZAy+pWYr81I5F8xzzhIuLI1DN0mmC0hVV+dDTQw9AzNSoSR/ANICZxkDD6OLeepNkhLZ/h7p5sd/ue5BWONH7mBtKFZ84e1c/OTwJyteTPTokoifMWS3AS1CLAau6Uy6QFXtFz/WhmtVj5MQ1axmzM9asKaJghl4dOi7ScPYLXTluWuAJH9azpjrw9qL4PR9nJG6chRoJAbkRYAodXhT5LmVe64CyWS8GDrAjGOgqY5rmWMJQJgjPJEFVh7xX5yP6tok8z/7K53Rj2XtsCXE5yM+7WI1/l/GSv0Pv7A5LRt/8mndt7tUtg2ez8UyfFsnuZDzPlWwkWCO3jLHLSPvLxYK+X6CiPHKqcZDuvGqNQUTYC0kT0Dhv7ZBPZvVDYgvooA9KgPYT2J1um0MPctIgIzk9TmXCT845IL4aKWOwwMPFkiHc49btdDzCPmEJ9IAI++yUb1QYcnXRhe02jcMcj2t8x9Bmdu4cW7c9syePqWYpytfgsAhCnbT9gfqOoLkrPxmvNP86lbTYLq1KcFFIYIqOrG5dmV6kQQlvbzQbaP+zIk1I3ACOh2KYd1CZpE2FiSPZ9yq7dZ5LZdKd38tM75/LPDQwrLNKvg/yvm5CAMXWNpEeCSfLYUvhSKmFgYVlBLd1aUODOma7pWuRH9A+3V2Sg3rqn3ux6yHASkqvVZN5zQZ38lwvHgfc2SsoB5OkAXwAKPKC7YajsoU9p3VNHvdf6vfL12udGeFODqGqcKbTHTMJC4Q/U4/Ch40HMFbnWdTIK7+Cr4bdQVsML5kUNEqxFSoIO1mcADBxXFP4rIG/tB9GzyQZjmSHJ3bnKvBtvVih+vZZMju8cxrFtW5qedAcDw2qJhhN0dkDRQPOdbq7KkgPktbqe/j1D7NbetdGH7qJ4eeCVb4W7MZkWoNJdXCII16vh8Wv6nzZBK2ItYyY+sKGv0LHR/g76c33zyNzkGOV6AeKb8/wMHfrq1/cqut2T9E8vAPLU=\"}}","sig":"e8418147619db01eacb6f22e5d363226a5b650b2df0b2facb3c33b8c93951c08d63f76a2441beac1facb213e569ce0ba3675907d8d2348d9f1631b568aaa6584"}
{"kind":22668,"id":"d05164ec247e35c2dccc14ae8700bcbcf5355d1f9fb589b97939d37f97f7b4ad","pubkey":"c938fb345106fb08fe6680f7bbc7b2e635967c636e763f6579accb3d0627ca73","created_at":1785676830,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"LE1qO6qizx5jMA2erFhj\"}","sig":"708e7d3c91e43ab8f65630208c294307d2a0472ce5c8e57cc6ba0d548dec4a0ad6ca5858b5de1f98bf52ad7f9c5ff86b61b2c80775edad0a8e251d24d1f0c9ee"}
{"kind":22668,"id":"f10a1eaab979692852318eade7c2fcf8ba3cec9f659867011647580bea0bcd99","pubkey":"129614faaeaf11f6606d45e39a591fd02f1a3d297cf5911c39a007c7c7f3cedd","created_at":1785676830,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"ZZEfCmLfVXsZXTgS8Wil\"}","sig":"fc92eda249f298ad247000192e68a7260e25ee237429cfec40245d4c674568b6938bd9a3ee9795a432fe81e5565425802a037f2bbaeb66d83d46977c968021ea"}
{"kind":9735,"id":"80ef4548d68d36095f62d0c6081b02a021fff27d628262cf4bfce334f8dee039","pubkey":"be1d89794bf92de5dd64c1e60f6a2c70c140abac9932418fee30c5c637fe9479","created_at":1785676830,"tags":[["p","e4146d1d7fb2f7f6e6b3c799eaeaba6f7446fd28ccea2592671cb239a13f4d81"],["bolt11","lnbc100n1p4x73qupp563fjv7p2j9qlh4cmkqhty5808avtjhxvr90mc4rhqxtpfyly8ysqhp5qq5hwkrnpgm97lt4sfh7gt82ccspge9mvwjrr45y9ej6a80tsnmscqzzsxqyz5vqsp59kuq05evhglk4j8efpcklc78pudrs3tux6slfjjnk2pzdgam343s9qxpqysgq25yxnew6a0humrzhg4fym7xtrxy8f4s23vfjert7wq4rkp88pf8qhe9kpagpt9qaf37w0z3ugfnjeqg2pqew0y2keythw36l6f36t5cqvu99dn"],["description","{\"kind\":9734,\"content\":\"⚡ AQSTR Reward: Btcnews earned 10 sats for like\",\"pubkey\":\"6c27a5aa02b769c6b51fd7edd80c5b21b9ba32c16d913b2bc8e1617fb4573680\",\"created_at\":1785676828,\"tags\":[[\"relays\",\"wss://relay.bullishbounty.com\",\"wss://relay.damus.io\",\"wss://relay.nostr.band\",\"wss://relay.primal.net\",\"wss://buzzbot-relay.bullishbounty.com\",\"wss://at.nostrworks.com\",\"wss://nos.lol\",\"wss://libretechsystems.nostr1.com\",\"wss://btc.klendazu.com\",\"wss://nostr-pub.wellorder.net\",\"wss://nostr.bitcoiner.social\",\"wss://knostr.neutrine.com\",\"wss://nostr-verif.slothy.win\",\"wss://nostr.cercatrova.me\",\"wss://nostr.einundzwanzig.space\",\"wss://nostr.wine\",\"wss://relay.westernbtc.com\",\"wss://nostr.roundrockbitcoiners.com\",\"wss://nostr.land\",\"wss://nostr.mom\",\"wss://purplepag.es\",\"wss://relay.lexingtonbitcoin.org\",\"wss://relay.snort.social\",\"wss://nostr.semisol.dev\"],[\"amount\",\"10000\"],[\"lnurl\",\"spritebeet37@walletofsatoshi.com\"],[\"p\",\"e4146d1d7fb2f7f6e6b3c799eaeaba6f7446fd28ccea2592671cb239a13f4d81\"]],\"id\":\"6e64b14ed5da47c6d4ee3caf4649dba7e9259ff2173fe9a092d46f838dcb4984\",\"sig\":\"08396838f0645989cba3c7ac753aa5a39cd7ea113ce588f4ca39b1061f1c17cfed46206b91545912de5470ee556d08fb2218f34593cc8abc2f7e317961407dde\"}"],["preimage","833bb4c5c573dfe24cea6306294aec8ee1bcf903c4b1af0af174aab14bbe91ae"]],"content":"","sig":"64460054919c2703eac063ff7dd4807610f123dfa6a3a5c838e87ca7686e9d8c697f5f135dbd4088d1688caf0f79869603b80ccca2b6c7a8f32d6622554d8651"}
{"kind":22668,"id":"e93ca07e3919f3f216dcf0a2e0a96a016840eebf41e5fd706773903af39c84e8","pubkey":"dcb7a55095f3761726ad6d5a53209c3ac46cf9be9c8e5013313fce6de948ee33","created_at":1785676830,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"qLShkXb42nycV1mZOj3N\"}","sig":"de497859d5a80968957b9cf61624e6ac7451d71249cca0a3f2945e888453946aaaf1d42844ab01e620f27c0c1f612d8eea41395449b4354def8eb6f1db91997a"}
{"kind":21059,"id":"6275550f617b1e838bb333227f5567e49c5039508ca2fbe67392df600352fc4a","pubkey":"b6e091b72c36a10e1b33f22e798bc2c497fddaf4334c8b90d9064cd40c969da5","created_at":1785676830,"tags":[["p","2e26907e7e535fadfdadbb3c5c2ca41f39c3f86a78143642fd0e435a4a6da6e4"],["expiration","1785676950"]],"content":"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","sig":"c80ff23a16b2aaa5607fd4930c15fc2b5dc99f5e56d4cb38e4f17d1322fd9f337eb2e82573fd6cf79f7dd1effb1facbed1b429cca561500052474979e5b87050"}
{"kind":20004,"id":"a44cef5e7a3d808548ac3300b5b214d2307c957b1b02c106bce6988fe2a760e4","pubkey":"4e8ec641f5340254c1228c02d69995ca9246c7b3b0295328cd6ac418efbbd9fc","created_at":1785676830,"tags":[["p","04504ed6eed7a46e07b708e8805dcfc2189a49108e3f42ff1137d879f21b6824"]],"content":"zDcS1eEbhLUg6zfwDZ0vRCaLCfKkltMD0OL-e69jva-BPkKQrHIrpJITgSq36FBl-cTK9wyojnUUzld76_OJDBzin-8fJSU?iv=CZo2CiPoKnVD_9S8dF11mtAOomcyZYQn","sig":"cf941df89b47a2144d2650a8c6313c976845e42d856136be7e932ddb5de816eb84ccc725f9237aa4f0a6d46f7148422bc8e956d133022bbe021ddf4f50a71fb9"}
{"kind":21059,"id":"5d90f55f4d87f435f650e0da26e501e7d2376c74b56e259ab807ed3d89f4b313","pubkey":"90e84244c692310d3b93fde994d7a25112a00613f7e6ae8d18f141e66930ca8b","created_at":1785676830,"tags":[["p","08e99116ad58c5aa5e6e7dc40668d5d7f2c080fc9fd697ec0aeb76880cf54bd9"],["expiration","1785676950"]],"content":"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","sig":"c1ddfe11c41e265d28ad6787a6f81d5bcbcb0038367424bdf047c78dbd349bb375c6c4a758a89644faecaf94b56d8851da1006e7259a255a42cfd539268a5abc"}
{"kind":1059,"id":"6293744ffa82db6aa84f749cf1a40f069297865f4f47e4b9fa998afdafc91d62","pubkey":"1ecbd9f1a29cf784475500f5bb060692c168beb8a73e7857b90ca4e9e39235ec","created_at":1785676830,"tags":[["p","15df570d3edadf50eefbe7d46ab9896356e52b7464774cfa865262f912cfd577"]],"content":"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","sig":"dc3abfc7a4d9a7689a82a945cd48dc7ea1f315254928111ec8a477f108fd28b59220c76377174082a5311790fbafd13d58018394399e28f6d949af95c4e59f25"}
{"kind":21059,"id":"6b85fbd7cd052c7f39df3de5dfa3ab7959f2a7b2131c22c9f1d03b9f3d11cbcd","pubkey":"61612c62844bb7bb89a96d59667be268c8db7f5a6dc4dc1dc915769929704f93","created_at":1785676830,"tags":[["p","48092008b3f8aedbf147268ae9a191bfd003e97d3521f706d738494d55a05079"],["expiration","1785676950"]],"content":"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","sig":"0c74c118a80bca7ee3ba466eea0cd2ba45c1877f8cfce631185524e2a40b7bbea4f01fd2a4728c1ab4d503ec6d2b5d4438c74314ae18a7e506f7dd804f86f59a"}
{"kind":5,"id":"e004071b98835065bd79466e9694e922eca9fcaf1a9699756da6ff7af1d50f9a","pubkey":"f32f0405b1976474d2eaa74ecd15c257dc7c5854e405996526023a7aef7f9ff0","created_at":1785676830,"tags":[["e","c3d841341e03f0f22b2e6e7cd0811954f7590ff69c785965c2afe1e91cc6558a"],["e","e7558455c28020c88895334d03cfa2567922e7010873686a5d988b73fdc412e1"]],"content":"","sig":"1931a895da1d66d643a2c3ea0e2d84f7c84b75be0c480f7900db9925ad0ad939efcf188d443ecfdf318649b5c5485cba9630d283a285416fc9b137153c4a52fe"}
{"kind":13194,"id":"a3fe853699b7641349db48aaa2f100a6d3c1c4cff0f7d6fff89fc1647d6a2a32","pubkey":"d03ca1662e2bebb12d280299b09a07a9c77450b7c5b6eacb357e3bacde1e14b4","created_at":1785676830,"tags":[["p","d03ca1662e2bebb12d280299b09a07a9c77450b7c5b6eacb357e3bacde1e14b4"]],"content":"pay_invoice multi_pay_invoice make_invoice lookup_invoice list_transactions get_balance get_info","sig":"000f134426656ad3b8b5a4c4b1cd2a208bebf2392dee1763a339d76ebf4585d847ff8fdb0793fb8fd7aed2cca83a78ec21d57635447d353ba5c528a351974123"}
{"kind":22668,"id":"c705e88ba63b117f281ef394600d2f33a03c72dd67c22b8517c689e00853d722","pubkey":"651ef346f974c18f3a781247a39ad0c1812f7817f52c3e1e48d09fb277c62751","created_at":1785676830,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"gnZTneCwL6Qb5ZD4UewN\"}","sig":"85550bc17c5328cafd4f1172106e4e7af93122861b8ea3be462271b6747e64b5baaa6c952ad12c2f4d7160b6431aee4dd3df77e850b45212292816f1177c1a27"}
{"kind":5,"id":"1e8a289daa492596bbe1d34fc8486cfda8f284883d0007599c21655e1ef7cb01","pubkey":"c3d2d513091f404497181534e7965d2b80da38f82ac76bdc8faa2ee9b20b7fe8","created_at":1785676830,"tags":[["e","b7e189df82207c6e3c837afe4900dbbcdbed7e8a962fb24bf3a9ddd2158729ab"]],"content":"","sig":"824d9bfa8b1860acb395c869895e1a950b8d067336265f28f164cfa11ba00dbf398913d00e2acde7f9ac4f81c34f13dec78394ad37376e916c28cd9df6574e60"}
{"kind":5,"id":"504765ce365092825d7185424246b11b1b510ae68c663424a47117e64ce896f3","pubkey":"92e744f3d9e8ca5888da31ac871ef9eed2a0b198ba08f417937cbff3030b0000","created_at":1785676830,"tags":[["e","84780070c1d4646031d9d096b035b73fbc5f1c8e8cf88708b0ee5260dba22e86"],["e","b7e189df82207c6e3c837afe4900dbbcdbed7e8a962fb24bf3a9ddd2158729ab"]],"content":"","sig":"f15d8a5be186bee060ed55fb432679cfa829a5e28f7f142b4004d5ed3c5e06cebb8cea9813cea8c391b541131aac750e4c9c3d189451de67068dddf6ef946603"}
{"kind":31990,"id":"b982613c77be9bd1202a48c630833a7949f07ee83fb76d879d4686af2704d8f3","pubkey":"867a67b1f464d9099e009e73f93ef6e5df8091be0b149bd38c85a88d29af7cf0","created_at":1785676830,"tags":[["l","hello"],["expiration","1785677130"]],"content":"","sig":"88bd7ab0d68817ee099e8d24673091a41f817f1f3c8bf849d2c91d672a35a3b191dfc52ddd485b889b6a6fe8c9654d95ba31591e32029f4490bbdd222526429c"}
{"kind":22668,"id":"22af285ccb3f6287cdcf363ebb06b648add8ebdda93d4ad49a40330abf8250b2","pubkey":"f50ddc7a8ab4a5ae2abc2802f7e33afc40f32acc157c749665cd6b78454690a4","created_at":1785676830,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"MUzRfOSvVHCPwAQ1zQlC\"}","sig":"77518b61f5756b7fcaccfd68a9c63164deea9558b0bf9fdab40d79fd9036b946848fc85241dbc797c11762d24b62265da569fcf91244c571cf2b070e53c8dbbf"}
{"kind":37195,"id":"784c21c5b8d1ae8569d4b663711c9579bc92e6417daa76b160ce8fb764c7fae7","pubkey":"522bf69c8bcc74bcf6e8ba3dd4e2322fce057892ad9b9f81ce93f05e65ab7564","created_at":1785676830,"tags":[["d","iris-chat-device-sync-v1:fdacc68213faacb25010d10c2c6bf9770a3a6006696a8282c906c6bc538315e7"],["protocol","iris-chat-device-sync-v1:fdacc68213faacb25010d10c2c6bf9770a3a6006696a8282c906c6bc538315e7"],["version","1"],["expiration","1785676838"]],"content":"{\"identifier\":\"fips-overlay-v1\",\"version\":1,\"endpoints\":[{\"transport\":\"webrtc\",\"addr\":\"02522bf69c8bcc74bcf6e8ba3dd4e2322fce057892ad9b9f81ce93f05e65ab7564\"}]}","sig":"591246b446dadbe3a2067fd458b72edcd7db27fa9889b22a5b032137bcd14c814c8039041d7ebc1678bb73fc0b8baf5285e54ea09276db25ad1c713ac48c4ae4"}
{"kind":22891,"id":"c4f37c58db11bd3c2d90196f859b88254d8953e85ea2e319b7367755c825ebe6","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676830,"tags":[["x","6y2v464t15574k2a276u1z5o2kw4m6i6d222e1a"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\",\"offer\":{\"type\":\"offer\",\"sdp\":\"202,14,200,159,96,222,82,153,64,34,246,116,22,182,211,172$FIiGiFA+45swhd9X7BA8TJjG/LGiO079N5BFevU3Otgzgz8iB+/cHqfFuKfuzApWtHPEqlVp06wXUEaLjvnbAkR6EBEaZP9ImobFwwLkw70gvM0zegLQCxJNxFDUOCvZtONUTjyFneKIqKkM06DTRhxOMKiuYKPR0C1RpvTIwfCnZv89I4+bQPyc56SdHLrAVX20SZO0YHJqe9c6cf7BGLlLHythEawKUNuqs9KK+8KLzQ1THHOBWjYBkvho8bBAzChGkx/tuqt58JsBjkCDbrV1QbhV2sfA6e0ypFMR29KLBU26mQLbMllAgdv8YCY3ceD6a3pLQ5njmMZrQyIno5GC0OWO/14efdYHF88iDknVdgprL2zhL00emLviW14b+lV1UnuedFROYBbqzidDIadnNIPoJO3bxuUIf/Kb+P3oKV+etfnz0/86cY3vuno28op6ixoNjt93v+3bsdZtNTVto6v41qRWj9H2D8Ap2TYpqnAyvZNCl7F67K53ONVxqw5lC2DooGfsUP7nbPBztZrkWHqH7pZ8y390FqEBM+QoqlYT3p562xAM64LYUe3ynEd+cUVOdJn9WjD5qIbk0dvl3N6ykV/IPb6lhWBN6zDrldR+gbZ6muhmJf/LOogRZtEVmYqvy/ObJIJukPBqTCL2TEVgPlmhNgH1FXzl7tIRlp1mI+aIHJwXdaAL4w7pbxP/QxNKru1WUAB9HJI2gasx0NEuFLwD16noWeZeSZfbZSgmKE7qQgHLIRRkp5qWk5AIUaUG0YTiHAaTaH4HakK/pEx5+/i2Ybz4BqdlADCDCHH7ys9/D7l7Kt1UoEKL2dbwrSFjAM2kCyVy8EHz6A0ikXv6Db8yEVLKgfc5dYGAgoe9wtHxxFmAuOq8yq0yVMKDvXoJCN5FlVNh0V62GXE+2My/BY9j/VxgOGYnXbRPEEaMTGqTX4RH1+a5hM8aDivK13BIXMC8j0/F3WMZOW8yP9WDGugWAlMb9Q8yTwVy9upG1XMXFJtCUSGHEhx00GDr3nJZ4QHrjpu/zAIvrBwXWxA2HNMumhbx0lYXUa6Ldpm7CoZMDQIXcSEzypxi/DD603pObMSPJE0pAyLIDdlTJntl9DW9Qmsff3ywVTAlc5dZEBaDmurZeretP7VGP4Q4dx2kpfHrUH0Bm/sNJLRZJIFuLseaOOxDn3s82f0eHNBrrNPorNEUOo0BHixswtEcOO7sYfYUvLVeD239zw47wVvYD3l3oPx22sbNG4i8d4v2B5QvGZYzdS0RTJUKgfJwIoYauUCYIzvhkwZskQvyKIlXcg==\"}}","sig":"292aee436ed0aa9625cc416ad5aa90e2b01729a096d66a13b31d7883fb8cc40167b6f12b90d6c206559d03812b347f85c64cdc00bd91692279b8ff0189930ce5"}
{"kind":1059,"id":"92d46f9c73844d0e73a4da9678fba013135470043952ee249b0a1b36189768bd","pubkey":"af36a76eabe37abb29d257916b8004817163931e8d7acb6e14ee83150b6b4b7b","created_at":1785676830,"tags":[["p","f6ddda433221cd4512a1dd7ed37eea610422964044cb96a739ef15f568670367"]],"content":"v2:5D8IFWcX1XB94nMgs75pjKMWV-qH7kJKJ7cp922s7b6dnXZNRUjTXSY07Zf5cDRkU-NUGfrixmrKSeCH_9m6Oboo65haHfHck4IMZFVikRtsb20ZmDKWmFz_IchtRETAbjUBrVFCA0Pz1M_VkVtH59RjK4E1J7ij4gw0lUacIKVaBau-0ujZEouYRLXtEPKVLuxXI780Gya2OONw_m352_AU638i_KGasfa9SMQZZdlR5E1YcYJi6-sSuonmpYdqrq62EXOue5ovHujxiTxC3Ra8P2xvcMH_VwI4-_e8dt_EQ0EG8WkdqQTEwETT3h_eed-KkuCDXda68mqpbywpJSzJD_R7ys79x5vEwkvjUiEFKHiixaXDAm-s6ssYxTVc7J7vPkEFkYIaPqi3ClS5Tbnf8Iv4eLngmFrABUtjGip1XHZ_2QVVv56-2qIjra3-4BBKMSq7qm2eQslTJBgbnuh9zxdjfhqAw7yefff4-hVCn2VBTr3EQdd2bsILheXzWOEj0vePQ-OAG1WPqWD3ruiXBsJPyJs7VWsPP2_PyBfrGWCvamVadPdekc-vPjPrX1qkJustPn1m5AOTW_pFmC8p-zVe-HTPTyNvtkXZe4oOIDBTK2sctviWcrrLF_3gQewIlOD7_KRFPUA6LSMXcK3Ns7saa-jhVfxNIz0nYeW7VgxLbhgveozh7B9b9kHTgTHOcQe6_XEAa3wSHXosYYRJUe0NVMwUN5OC-mmzwCYniyDnWbinTqOdx_EqVQBOoX1VPh8eMqEv499HWnbALCn4iNcY0FA1Qe9KanHpfC-VF8Hz7hz0KB_ZaqNCUSTl0SC_vHmoeEnx_h7Shyn7vdloBXvdaaZjgDfp0otcrC_oa6ERCZg2YEkwqnmytmHOVf6zoY7OsOoRFTTQtpvd4cintjIqVg-V_WvN9M6Bth6g7iTL8Uh1aTDq2efEOeto5GHVGIQftL_uMwSjAAlphCv17a_EreXQM7V55f056Z5OIQAbOS-tXkoZ0O9SRgQurDFsc5mC39H1GoVrpPofIGUFFAgnCTB4mbuw-V3Y8xm0_MJG-xEE4fr7ymp2syDpgCedy_QkFgU9-0fv5FaDH11uWs7ulJlZ1o_WHuzC02ydpOUK_2FseAWTYw1KhN_9ddes8d7xBRF8Ta8pLPbVBNYwOitHb17cspuv1LSind9bjT0bH6dBx0vD22tHRIvkieIuHjtypcz8U5NRVmbchmw4WhpvptvWUoVqySxibpLBNTbUVqlBxXJole6_qkpnDOn1qkGuJTN-tWqQfzZyhXCNULWZSlZ1NYpBrSgTQTO6QBh_tr9oe8Fc-oip7_c7o9gxcvYtmTgghqW7meYhi_tb6dO9b4JM8VAj9VoiFPd6gBXNSWTJ2ej36aqqzF_d_KEgpVr8a01-4owIZMPTsefZCMLzoMux-q_aYfvtvhs3wM7YYHWfyIw","sig":"52aa16a82ed4922de3a8c78fcf5be1066a328cd99ef0b2268e3133e9cf575605c3c23b42d6c5921c00e19718ad446eea051d4fa601527694f01257fc53f830c4"}
{"kind":20001,"id":"0008627e93710f902251efbebea4f25418fb8263eda4cf113ce40d49100593e6","pubkey":"d4dd33c3e92280a7cc8a4bd65e5b3876a572218902921df450db07badb1bb00b","created_at":1785676829,"tags":[["g","td"],["t","teleport"],["n","Boob"],["client","glub.chat"],["nonce","1534","12"]],"content":"","sig":"5175c979a4127301f00119c041ddb30a0b2836485a1d05d824aa933c84a4775ef163588118cf8d9a09f805860341df300ee4a8e9471443544946972497e0e80b"}
{"kind":37195,"id":"b3848e2644e70a784d948eef64eca608ce57ba1a8a75ae5e26565ddcaa9bb8f3","pubkey":"14b1d34a9d862fa6073e2f9ad689ff11af8411751f71f10e5440cde3876c6710","created_at":1785676829,"tags":[["d","fips-overlay-v1"],["protocol","fips-overlay-v1"],["version","1"],["expiration","1785680424"]],"content":"{\"identifier\":\"fips-overlay-v1\",\"version\":1,\"endpoints\":[{\"transport\":\"udp\",\"addr\":\"nat\"}],\"signalRelays\":[\"wss://nos.lol\",\"wss://offchain.pub\",\"wss://relay.damus.io\",\"wss://temp.iris.to\"],\"stunServers\":[\"stun:stun.iris.to:3478\",\"stun:stun.l.google.com:19302\",\"stun:stun.cloudflare.com:3478\"]}","sig":"be31af4686ebfe3d850c516be763e74f2c5588a19427dcee1577b9a3b6dba59da5ac920fc6c6a524631e9e9bc32161b8de01116f34758055101c7cbf406c6995"}
{"kind":21059,"id":"7e8ac972c6a94ef4fd8a07ced607d6229065d421d6c480fc13e7a40979f981a0","pubkey":"7be527855bd9034f673f08570a74b91411141a1c07b20f0d096386f1f2552a91","created_at":1785676829,"tags":[["p","4571e6236c45bb75ca1bf6c142b996d9d451ec6c3fa14ce279dd53a827b6525d"],["expiration","1785676949"]],"content":"Aii+iv9ltGTj9CmsdoNvz6HpNCN4iElt0Xd6ETovdB3zPh1T+w8Fdcu0/7jK9z+WZwAfpxeMs/xEJGK1ZgD6xRPNthsqDZ5/j9Qmmi3KFB49khXg2t/cNLisbAdFgwL6R+0zfRb7qZhIkviXKMKeuMK16caWFjjzhYmbcsxfeOUCQILoFu2LeDk8eGBchPc+zDazmgWYQbMOSy7e05M1IWq9oUNNmPHcfwBLrct6mnUeaP6TMU8J0akzNVxRae91ICLPSspTbu36Pudc9UP3cbXI2XO7y9Qt0EV8qJahanlarP0627S8nWGjOQXTcVXTlPZXyXifcLQ7XHlL7q60YXwGAcQFIsrxjtlFUMHr0sZ3M7YSPST5dqfJJ+doIAzVuPDXG+j16fAY0Fub9Y3Dn8Wan7SK+UUhXoFNBINL7jhZZfJUcMVPMqslcIzp0urzWJSpRDW0Cj/ygxSPl8ucaIcUl59KvUEni/1VgRi7yQAeI0IVU/wz8z/wXXKsiJZReUhpVpAJh+ek3TfKdolInSQFLavm1KhCde2NtNOW9fCyUlN0/lpEuyKxbDw/xkINqQHbWuUhJtJ3u3cc7kJFxq9FoOCp8oM7JusiHY5wVGNbkxFsasaole06wPhtPmCJNRgs4KhBRCyVxRrKYf5n4knBQSKf3Kzl1iePH6ZaBXku0rwF/UPnmzqQ4vOX80epDv8A198GDwAJzOEi6rZOIODXuFl1/2etOVquRAsqloxK6Q3hPY41+GMaJahACf/Ip7KjrcgIsfBMPSRqjESbk/R3lw7DaMIPtA8KcY5h0QtmjSjwQXFznXPpABXx5C0/Oj+V7Zkp24mtlhiTpOLq9tTOQXHDzGjiPr1Ft8DKKfnP9ACxqYaegnUWtKENad4Msp3lIgMsqizqmW6PAvPyRPfvdhF+t66xCkkytrDghMyy0nDamU/p/m+uHQRMycjdLvMFW7yfTyLjzLov04IX8bYqfwuWy0fmYWwTe31cjwzBIEZx1p+WH4RPXzsaSNAK0k9jGT2WqtYBe7MMGG8VEhVRypQuNM6eWE1a6Z820LRWlsJU0ttUjn1LAPjbqE+tdJBlgAbF5U/ww5sUbDzHnIJKFAEBrKc0yaexPASVc3q1hnpqKK5RS1KnZ6tngdaVqJDkeRA/cSE4BTgHz3vnC9iVGFJFiyZkYqaUn7KlLe/QLfYgQ9Py01LOyR+J3IyPAsp25B/KxaZi+RY7eTozsetVXO2LEFV2NhoDOaK4n5+ijHy46sxCpGFI4Ml6kgFpqFxp/1i5LTN28XGHlF5uyVErbDk+2EMFfw+vqccAJzhl8PmUUorU5ybimQcNgtjgx6mjn+PsGylHBnKwvVccHPk5kCaCGK7b89mKPwrsA3/zPwIJDJEz81FY8Ue3v480mHV3FENdyKELB1QT9E6H7cZ4DfSHLycHMioRnPnB7oIinHZJy8l/k0T1txwfNDMbpn3o4mgBJL3BwcSdGCYKLVDlV6H1rpFA4QL2BwMCHMs5syW+LHFS/wrDA76T/YtFK1xHAte7nmvWAYN44QaU3ILQhkdg4iW5HnEU5mASi2qIzYQaT5IvEzsVdH8NeK+nr3bMdzlD+VMDAQOR0Dqy6Mi9KxkMv3yPvia0d+8GReksGG5NHnRGfYW8OzQ3oo0hXoc55j37rfZpHJvp8xZOjad6dq5MeUbMxEI81OEQfonpMt9XZYcbgdp6uVQWo1C/cD7TxRfU4NIhQPDBK2HMU30V1UNZmTDK4gwOBaaj2LFk+Xu8pU4g7qZycuG7bpZV8mlIvxyRHLm9P4ZZRxyISNWZX9D2mtyuY6WNrlvJrCcU5Y91Few/ODerkWyktqBkSlNqKDb7uwQOpdvipBzlyDga6wumVzEb3NFuzUP2Qfoy9Q2E8zFvjws0HFPRR7IUv9nSg8GUiEDTygyVzCWwSEVCDk3jNIP5GUWKL0kGCekDQEVq71g68nMGICTmzIByYuzDXrAmqYXevw37TY96fFjzny5uFyaALieORYkLfEmPozLj4kRDlxb0FiAMXT5J1BvgQbYRvKmTTlaIFjDfVbYs2d5uOAQ/0znRE6hj4b6KckufDeRomfYzZql3r/SwccRsbRkg+s0FQoERCr/gSicS6WgUYV7AhOonogzZlTeR0KX8bcvEcymuk3M6km9ItYdLL9JKorCIkF/tNZ/bduvf66mjS7GojmqWQp/xY9QnVpHGWo3LKXIReBLq30/UZ0/nu/duOS5+I0sq3JMfMk4q6vJxKTgGqeLSEuCtX/k1YjQXKmte7rOYW3ELeuU9/L6mrBQCQXhADZvaPEA+ZWH7ujOsrL5+XTxWtdha9g6i4EugmKZJjPIs04bEgp9FaIhqYe/g1LEZ3enlZDVFB7gwzVli0TtDvo3KwHWDQ6L3G2GxdPShWc/grZ0yRoYblglA8Je8jxp0Z6GeIBfA1QcJiDe6/4ao3cM+7v+gE6tJLrpj2520gfSE4yQeRpEUBoLA+E4y1p4xDd1+Rk/8Fhq5O7hetZpFXSc9ZMnX1DuQYZZRUZBT1BGdUU3Th+HpBdDCsR9stDgsBgiFP7xVthOm+dMb0xAV2Iv73lQuSZ8Mw/C0L7uUB0qjgu1j0SCtkVCVH/p7/8VqPbvGLOSD7HtevbK7jVaoFqYe7reSuOFN72vu6t4flsKyvknLnPjYsOyAhfEw+zRFRSFjFvWL08772TvLZCpzDP1ycNYC0YTZOOvvxUWJRj5tg8+gQohGVXSQerLciRe7WOD2XAfftG2bLnf7/t6zRHqnUouN6K5Rt4+fC2wdHrgL3FjtjYby3Ph1vmOu4fT1RcQ1c03QIYdTY8bRUxPAK1lTMXWlYdye/nm1MO0kvfgPZTi7iOHDu8T9ucNJIjUNMTUpNRJRgGsMg5o2cyNZLleviERi7LYc9SQ0wWgXFTJHoI9ZdHZlyx6vCN2cxT2Ffx0qH4uD4kipxGUqLuXKkYyftXrHeJ8MhgZ9QTYdQM1UfNdDyqu+czl65Ft3Po3soFcOoIIwmg0s3wZ3J7MsSxYlBBFrqgSDlfkMPsblaYvtaTWzQL3IfsnBTcvVIsKBzhm9FmEKUUhhL3tgS6ag0cFoBSGhJUhkrjXO3B7Bg+Or5bEKOxA0ZkEjiCyqyiy4BTmMvIE7aNGm6ayh/j2x7SfJQMx6IOkDDonaStViJiBEJ2IsAQ4vXPDkd41Piyv5xtYOFKV6qMmHVdG+aQbnS43lqGtSYA1pYTrAgt5Ws1H8q0gyUG677cAx+Nj2kr6ilro/OcFl03IjWs4KsPfIpZXoJnhap5WzRwjNm+PLf/mk5hKjcK6QvlCPuXeySjTpVL1FvYzAHiXsF3yMdpdulxlEu/6hICJAzAn6re+UqaSMgsWtZvodv0fDhupjaNH1pwQAY7Ag9mKqWFN0fAI80K9YPOtJMHO+AM+6T7jIGD3Zb3ZXhshmbPGlLzFEKlU7vfgrhBoAJbnTYWhXELlD5Tst60SYiqs2okE=","sig":"8af0bb1316d7577439f261ad76875b1babedb47262565eb2a8276dec88691a80dd64e2a894dbe78562a4b69696141d860ec6c9f890a14c84549a1225a38fe47b"}
{"kind":22795,"id":"b0806a8559de7756b708116bca32573e9a11d6bbdf0a51fcff4cff7628d7a6df","pubkey":"984094d4207e9130fbb6c28bb438e2cfce471ae19c71046d6950fd57f20179cd","created_at":1785676829,"tags":[["x","s1c1b5i40406k26b1f1f5y3s3f5o4x4l534s24"]],"content":"{\"peerId\":\"5LAzUDGlviJBcbB5kzJl\"}","sig":"eee99b6279b298bf32d5bd110c24176560e6144d36d134fb2c981eba9c8bc803378ccd154a31620938d1d90a9d9dd248be70321ebb4943d1e96019d40244d190"}
{"kind":21059,"id":"27bc4a662ebbc319927530f64000150760e270bdce69cb12b442529ac33ca680","pubkey":"73399d3a149703031fa0d4701ea9e319226c2979343e89c4915230a533572656","created_at":1785676829,"tags":[["p","fe3cf170bd88be377f684d98d098aeffe3438882eb2a81013ddc4e66ea00dc46"],["expiration","1785676947"]],"content":"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","sig":"4813c3412e748c9d99e4ec4ccc6b667a4140714a094914df05d111025e442bd30a594714f03cb0df3537ecc82653c7045b98531bbc0cf49faec2bd67a107c175"}
{"kind":21059,"id":"e323e7671e52fa1f93b1c38b74d9045f0e7e0c57c5eb64f0dd9dc0aa4cb7e9f2","pubkey":"42dcdcedf8f9fb5a343aca2f324985960a296f89a40b186ab2663ae544649098","created_at":1785676829,"tags":[["p","db4332cd601e12593fbe239cc5af98f27401d46012c778694c22cb147ae3efdc"],["expiration","1785676949"]],"content":"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","sig":"269bdd6b0a63d8d7842c04b8f6e743848caea5442047c3bf9aef46d389be02b61d6b43a2377b33f6ed6b199c64f2775120447c2281560ed6cdffd14c33192476"}
{"kind":21059,"id":"80b6cea3ad0b0abedd667b18d3804879c9b053bd309d388e58aad701d3c4e129","pubkey":"764da1376080b4c50b442e8682283e911a211675ac019a354eb4abcb3e6febf4","created_at":1785676829,"tags":[["p","c90adab9315b97f7bdd9bff861bb026c9c454be1f1fb3a9d64812b75ac5e2bcd"],["expiration","1785676949"]],"content":"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","sig":"30e2bdf73205825bf3a19ff6d5b0fffe49b2d427470d7670e9464628f58791f491b41f78ac8a137ebbe64fb0d603c1b516e857ce8561c8812e53c04e313cb3d5"}
{"kind":21059,"id":"7d821dae2407dc0ce17b7be76558fea2a7b42332a80fa26a775bb481e7ab4936","pubkey":"51ab81dee29a9a2cdf3adaa4820d00f4018cd540d6c4b6f5b7cd70a155193e7d","created_at":1785676829,"tags":[["p","4571e6236c45bb75ca1bf6c142b996d9d451ec6c3fa14ce279dd53a827b6525d"],["expiration","1785676949"]],"content":"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","sig":"d2ecfdef11672886570bb8da9f00e89ed488351faa377f7cfd9625455b752d6765a1415ef351bce454bb4c8506e9a7795572fcd0ca3799a5e1c88a96ad164287"}
{"kind":22668,"id":"e5bd8d0ba4903407e15dccff8c3847a142403fa8808f08d48927eb1989630530","pubkey":"011290d507f716e7e28b78431c6dacaef737ed0c296eea006b3aeed76162aad2","created_at":1785676829,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"p57BF10Qa2QR5CWwRPwk\"}","sig":"bec46000893446903a21a2cc3c153fd0caeaa2863d420f613d5c86eeeea86252a977c415c08782e52af2dd68e12ede07786e437f4b4e5995d3cc9dab901715e4"}
{"kind":20001,"id":"0f03ec93ee1b132d83fe87a2a11488a2457ee29c32d012ef86d6499be2b6aea2","pubkey":"b5c861e028296358eeda540fdc4bf0a55620fcfbc8402c2aa6c0e374ca4caa93","created_at":1785676829,"tags":[["d","781ea0904b118a95"]],"content":"{\"e\":\"🐝\"}","sig":"8c1f43b54758888bab77b247e125d39cbd0f851a9a866e8cea094740fa972dc3545245e97c3b013a10c4ec85ddd162698c377e4335b17f9e3c5e4c834e5d8469"}
{"kind":21059,"id":"efa8558e04b1018251c1ca9f75ade84c3665236ac0dd4e7531a5aa4ac50f382e","pubkey":"61e46286db0f8b3a657c97a18b24f941c2bfcfee82e4dee7c43a27dc73d9ea9b","created_at":1785676829,"tags":[["p","62cf088f0fa0f198b341ce6c3c4d16f1bf06c4c94b11b54e5e7ca6290e22e94f"],["expiration","1785676949"]],"content":"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","sig":"6d06c924a3cd1d73deaccc52a86ab472a3a30975ab54311f5e3c83d0896f18abb7c921e5726317532de6382bec35f2237c4c225c03abb8cdd52fd143a1c0e93b"}
{"kind":21059,"id":"cac1ddbfca49abfed7106b5e8b35799e803a7323c88f4ab9311c55904dff8038","pubkey":"cc06b791544dbf3924fa4839b43e6db9df75329b67278fd32963cd19e9cee582","created_at":1785676829,"tags":[["p","f3d5c038e4ce5015ff2069c5984c8a47571a427b40e120d5c412e6ae6647744d"],["expiration","1785676949"]],"content":"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","sig":"0648a3e97630d479672873ddc45636b50fda69d79145c6cfed77d518a27808417169dc87ed861eb0e6ca1a8bb274dcd8cf970b37cd3f0d42c09ff7d71be20e4b"}
{"kind":22751,"id":"cc1b2bbb1bd8ef560837d280d1510d93d5199aa019673d00a871aaa14bf4c848","pubkey":"3d5ce431f2987ab0f0ea0745e5703bac780ea9384e90c8a198ed5dc8978f1ea8","created_at":1785676829,"tags":[["x","5bb4v4t492d291p6m82o5g2eb2m4w1iho1h"]],"content":"{\"peerId\":\"KInutrde21v4Es53GmBx\"}","sig":"b3875007e6999506f815c52162492680a92f5323dfa51358a5481174692a5f53cee4f8b6bf295f0b69704c777c2d15963bc2fec70d5cb1238569e3189d1097a3"}
{"kind":21059,"id":"ce16cb9b2bf391663f91c94f158745bc94760d383dabdc35ffe728462aba63e6","pubkey":"d5bccb32f8efc4810802306118e3a2e2bc39b690f6e1defcc06e007b3da9e591","created_at":1785676829,"tags":[["p","2651be916e121c6baa67236dc6882371562f2dba151ddac18948b8edd88c1321"],["expiration","1785676943"]],"content":"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","sig":"67272aed2ffbe1417bdeb2362a9d49e6535253b5792ac600ea3992395b7fed7cae545a5e14ed4a0f037390707f5d0e50c6132b07a480249df008e13e724b8954"}
{"kind":13194,"id":"3c0da6d6cfaa5f198bf2dfda454096968434028808a2e29e760a63a824de62d7","pubkey":"18ab43661d7848e6e3843a09c6aaafb007af2ce2ff3481f81469ddae1e8e8fb3","created_at":1785676829,"tags":[["p","18ab43661d7848e6e3843a09c6aaafb007af2ce2ff3481f81469ddae1e8e8fb3"]],"content":"pay_invoice multi_pay_invoice make_invoice lookup_invoice list_transactions get_balance get_info","sig":"29c9cef993fd1012c2a21691671afd32f057f9364eed31b40f0906348ddcc6606aa5b2e4ef8aaf9198e8cc97488cd4133ae650763a3b8da3293e72e07c9801fa"}
{"kind":21059,"id":"38274f3cf136f9a19359e866d55b86e661b9c1da65c9e826c08f7669e84a7337","pubkey":"931128665954f1a3f0fa09a5e3f371ab192fe840575daaf43040453d838285af","created_at":1785676829,"tags":[["p","2ec845714f95e64d1de364181734c2e58d33e0534c33402d2bce2c0c10a53b65"],["expiration","1785676949"]],"content":"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","sig":"77927d477d07993c9ccf912e488b3384d69aba61b70f13c85cfbfffc2cb3fab7d384ddc4b377470bef2a93ad4635b1ebb74d05dc0c6f3f21aa15d4b6385ace43"}
{"kind":22668,"id":"4d59c8963f927fa9ea3c116a710049275033c52fe5eee1c2d970b1e55a021f34","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676829,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\"}","sig":"7a569489f112258890c563d4924c4f7344537aef198f64a7cc0b9f32b96ec7306ea98cfe1fff4fa6ac2a162ffa4944ba3699cf2751bea61b765ab32b90122651"}
{"kind":21059,"id":"5e5b504bc37e7a30f376cf1ecc6884e31163dae41f5ca3af1c7d5462e972dddb","pubkey":"fd5a12fdce164794145f3a5c4b35d798def92f98242ff4609673de9214f2895e","created_at":1785676829,"tags":[["p","1ec71c39a30373cbb10e91d6efa6ef1e9dad0756d610c1a4e337df016eeaaff8"],["expiration","1785676948"]],"content":"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","sig":"dd1e19594cf2758b966c27f773e20c5f1ce5127e7a6c046f40df32587a6294e23ce91cbcf789f25ce93ca17408ba16d17a82449d734c80d328ec53f096047e74"}
{"kind":5,"id":"782cd31915d712063037d3948ee8e7aabfd7560dd332227d502ef641cdca1ce6","pubkey":"b454400c0f61af322680e46825a10c69aaa4226be2f474dc21fab2c7271d4ddd","created_at":1785676829,"tags":[["e","9aa8c8f7a20f62091f9b3ba3ac8262c9c2bc1a40d87ad1c75f0683c0e757a983"]],"content":"","sig":"ca9858bbbd3a7d46a9930122981ad8402cb302dd4a1802df2270c115c12971fff8cbe9b740898d5f618f3f0a9ddcf79ff8f3fbfbefebd21d60670ce15982bcf5"}
{"kind":21059,"id":"3aed713425ab4a0459176d4ba589876e6a313d879fad18152a11347c22c1114a","pubkey":"842130f8fe9ce8aac0eb1e1f310de93b32e6968a5553dfd63eb5d8eb7874e0c3","created_at":1785676829,"tags":[["p","1ec71c39a30373cbb10e91d6efa6ef1e9dad0756d610c1a4e337df016eeaaff8"],["expiration","1785676949"]],"content":"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","sig":"aee0be687ade030557e86ba811b5d03c590fe6cd2a62085015cbb4391960b6a24fd0d0caffc5ebc5ba765006c3d98a5c847b15bd81ef38617d491a6a11cb6705"}
{"kind":5,"id":"137d08e7ffb7fe1cb7bc8b2be596e6b65d5699f7cd64f13a23bf390f70807c90","pubkey":"29d1d9e11edbdbf04538d90d053b7b2d32c1c253fc3df1893c11b68826630609","created_at":1785676829,"tags":[["e","810bde802c36ad6d9aa0e017ac0f8bbd6e66ad80df5b9419c0313be8054f2d48"],["e","9d28fd6286648d315a0c47db82e20b67e985a54f10eef09745d5cf0241b85e28"]],"content":"","sig":"bc21735717ac3f284a372f2bc72ca9d15badca8f7fd8016ee89015ec19d9a212f528fce8b16a98b62cb5108f86b5ae2698b0049eca59f70b710eecc303765544"}
{"kind":21059,"id":"16bdf72f80f5945fb5e08e5f50701615cdf8d9f016c8f48a6f8f22993da33080","pubkey":"7255fdb4025a1afe269132658221bc6e698108cf7b43571e929cfaa712063ebd","created_at":1785676829,"tags":[["p","1ec71c39a30373cbb10e91d6efa6ef1e9dad0756d610c1a4e337df016eeaaff8"],["expiration","1785676949"]],"content":"AvXP9vKkmShrzcY66dYAn2QGRBXXVOT910qrfH9UN9kru+l9SCktAs8S6Tn4cWDGQuopEDBNOEnvO+hbfHnxO7I1W2qWMM9x5VOLOZ54xXsZHilHdQe241IwLTZPkrRqKwcz8BG2Lbr9VcFk9JE19Eq+iDdBhaA9i0vArK1/zABWadRa6+3IfBvr81nai7BwYuBCFF1fAR9B88dFXUrhxu1UFuwLmNMt2vFlNuC7B4fDbvWiYul1LGb72co60yWXJHaD5lHifE/ywKyp8/XhrZhnBQvJLI4QPT1sF2vZOdp286MW8wmolmqHezF+603notptYfCRI7WlJyu2HJ5rh05ZZLo4FGynQodXmKcjRme/5SsUwj7A+d+08mmjCdDh0TvhcbzxUF+4AOoVSMGauyVYhwxPpGeU4pTnASZtREQnL8dYQqZfaKHyPuyXDK53EeBb6HxayU6pqncy4jZGP1JsXKF91oHLcwag8k5k0w0oUIDRPUXdXNggZHfOTdthBgde6u9+0b8L+LhURYx1y9pVAtbgewviF8uXHHht8ysjOfnUuvO2Pgby1W42Luo9G8tK0zMHxSlqtmYyYtNg1vxRO4YcXXVPz1+SPxCrHy2QANyjzG78IniygUEyetxP7sCoorVHx4dp/Jj/cOS7oxXD/uTc+ClIMQPEmdBngpZQwcHDZ3NcgJR75ujRcbqpj9Lyiuv0eFs4xpMHFEPF6XCu+MGVk+NguwxH98zhGOnUA9vFFPDRk2MeOOWv7pPnFZ79F8tpGmmTyvYZHHJz8Dgv2h/pmy3/qjdIBogG+EtXnkJxeIP6e+yEZNIgi61Gf+PQ3AVMgu+ogUmO4Fobtv7SvRbKW4ybbWkT/VZVFyM3p4uyXp/qVC1IKgrZ/woSUmmoJxGViKflb2nfp6bzXUYAEu9ncVIn/npfW1YfZQEmhfNVtLVn8CWo8AUiQNmHIG/+YseTGKmwRYb7zTVdAc9DTqBkncFO7BK2qX6o+f4iA/645QUejyut1u4TKNv44ZYVhNBr2kdxmywYzUMu0qRKrg2JI/6pV/LpYY1dBd/Kp7IceiBWGZSeWiJ5PXEVJysb7NOwYQcY5DnftLdy8ZplBHOGuxBfFTnM7eyke79zyGsqp/lqC3581sN32qt5aDT46GREKCIw8SQ/rXxyYHI3CSfUFNrZ1AaGzeUGM6I+d5FzHgNpY7U5gSA5JysRH6ES8Kmp61h7xLelVc//5tK56cDkSRfXY4xPnKjhOhuBn9uwrYMPb2G+nWag5OINaIRiIDrU0CtZZoSUtttScuFEV4ghlcXM904HpcqPdYawPYSRXlDkwaDV9tQsjjZEP3HStuRTL0YCDpvF+eotZ/BV2vmyckHpW4tJ9wcmRFiXWsCGYu7t6TIXyO0sSjp9rtsPJPntTRZSqd0Bd6dc7gsP9dBaGg46cxfpvfdQE7UaZyCrHuJNFh/S2gqu7gBoQW2ICs5DgkNVLxRqrvzSbEO7KowqsAvxveAfXt1d6uHHP6WbQ89InI5FCMZ+5u500XCxjetBMV39TvlOhpmHPablf9D09DDFemD39NHv+cyI7mdE2eygvQTbiICWpU8YqTNUkTADAB3zU/G4/n4CcNNZlBuSgsc4LsDCrUM8utUEaEk16OU+hJKD8jfifhxx2D1Hx7b6dsgqk9GKlU51VCY50m4CQOVlZByWsvl4BnHhHveCe03HyHa30PcZDlLBIK0AC5Md15FipP4eel24/695OFEwsK1spveOqto0TG3YHEJiaiSzMdZiIAMU0lUkcBQv2N7uGuWs/uFuWZqRNllQxHlNbjU8nAr6EUVGcXTe/T/NP7iPnnr2lAxrlcMjxp3/QcQfC93MGAXrpaSfsfRMj6gtde6u2AdDv3ndj9/QDiihQAwHI5SjRF2qh/8JjWsS+9bv27k5QqwLUkbLPgVws0b8knFRnR6ZLDGx38Ecyt5EaGG+C8yQZV6mHGfaGmytgYUnN1qdFhvzBSI66Bz4YMJDXZDt5Ra2f58Kamh2aTw0UJ5bgWeUY2DUNP5kw818dtqoXDM00XEiXLOFJsP/02asxJ1FSgDrldlI5vSMG9UDhvmtl0N1yV+i656lLaxhEmnHCLR/KHMUgP+5If2nT5wI5Zsv5wajzn8ikHN9m2Ky7/mOyAScZXCKlcNTmQSVpQlVZEQqvWe5IIllSoPzv5hxknFhdrS/6uI7YjDCaMkoh4mWAIR0urNarBMeWly53J1B+PrnRVZQRp1enIvB0szm2LelOe71aiMxJQIfNZa1n/yqwUnMQ22YWCdW7gHoBXqbmg+lBmbbVR7GVR/bEl/extCwigB0MW6ESW0gP5El2AR8CvnWp4Nd9QMxySKhWYjXSYiG+NNqcVOeYDwzlHcpHT2ZGiTpJbYPb/ipc7nzWwLXODzlsYIl4NV4uxmlA7GcbCgJmX4WARfknaJPj/KPQKVJZOi3dJRlkxBb73AlHwXZCF1ywTrbBAyhdI9G7NylkN7VtUgLibY28e2MvBb8mUBIZJE/bryzCGv83WU9aiXdPIxwjomu27xmBg6Uhuych5/JP/0SLfpyyVgaE1LGxeKAkqOsrWw3yWILYxtKliqKkpAdNW8BRHU3/dWsqaSJxws+q+93kXsbDTx2Pwl5EbOOEgSa3ScnSf+bhiDtErdbc9TU5/H8psr3tiHPWQn8G3PWQUIXec5RrGoIcjYN0NY/bGs1MsAtC9AZV4FwCVlWhMWG//ZbiqMBNHBISY4JkrYHjCZwKHMarWHjCy+bT2yNVXzqJ9vgBjHoKwk/iHe4jRbmhFPH7boDtgEG9vPy6cqggeTQKD/CQf6TjHFSHPpCPW7cMdRa0Sw+FLU7XQapGN1xvXXhOGzNCIJNLvu4HQ5/fFoFbbKFV+qT6Ma4vomWdtaaWTK689Y9OyZiNxP4NTgSjumYdSw8xgliq2JQmfZkseYoOnAbrFFXUMqIzxA4FPjSfYsb8QiUcfWRLNGdLoLsnBK0odAzVRNsz7AeCBDAaDWq418A2C5S76w88Mp9T47RMkMneURc5oPnBjQ24HdWnNYqa6xzZMSqv9or/mWAXcejHVTgen01+IRqJKkOnjFCDFMp0jT8wyYKUS6w6v42pVgLbwqWDIMd/KvzzG9lgbnG0RPs4eLOpjTLG/zdAxR7DBuLgoCwuwanuAp3172CZyOwiD2ul10yduneEFQUJxpv/mxi5rncMw9uWVlKvuCpZb0ZgULQOVno2AIB+ZKmPMj5wX6t4o2eEjN8niP5cvsoNbURBJKZDerUo46OHIw+14oGt+oTN9PsadReGp0+pRQ5veLn52oEsSM/wZ/JZrp/I19muHOCcHsVmcJQhaue7lnOjKmEFzp/jV7rBDhJL9IFA1oqkok3VOUJTUp1wpjbYPr/Fc19TLWukAP5huqOwvK44ljOz2A4hTqy1Weyp9/fhid/P/3caIK67jLBnQdpdpNbOokYNAszekxMYDHRmFeEXRswnNE=","sig":"e113e7856c06159dc5cedb6262892bc5014d0cb5fb21e749eb23509413548dc8df95cda93e1dc8833a3987e5227bf51c578fc990f3689e7c0fef9da250230350"}
{"kind":21059,"id":"9aa8c8f7a20f62091f9b3ba3ac8262c9c2bc1a40d87ad1c75f0683c0e757a983","pubkey":"8f3f5b88fc7641fc8cba3f85d31023c07562c05290bd8dcda72efbf444f69926","created_at":1785676829,"tags":[["p","de2474ebae76da5fc0dbbfde5128465ece9adb6174dc5210014f277e2c54a093"],["expiration","1785676948"]],"content":"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","sig":"857148777b63e5e0ee1eeb98b12539466cbcd35b21514484fad507c6230b9e71d3d565af406cd8b97b0966f7e83bb212ae21eaf8a57162b2f47bcac7c293e427"}
{"kind":24242,"id":"443bf61bd52ead74bbf7f3e0d147845f0ed9edf680334029c1252c41a58caa4f","pubkey":"0214d7edd25fe949a9978de3b36f90f7a4ec432dc9753087044be638e7f2d3a8","created_at":1785676829,"tags":[["r","2145c86a56b377b435b1a17888dd93707e0991ed524c613f9ba73541c94e14f0"],["t","P"]],"content":"{\"t\": \"P\", \"d\": \"VeNHJfdrPLMeFtNrqqVREOclr1Pj8K8tan/m+4DPgHeYoq1KI2ryYNcOurYaDy13Cq5YEJGD4EZtB0CxAl4Su48pTzXdmuSs1IHm0lBYtkHuxEhUZigIGi9aNYY=\"}","sig":"c350662d3c997e8238be51564efb0172005507fe2e73f882bae32108fa272d6c4fa2edc55866d899b01ec3944578133092220e5451cbad4d5ac171827ab1744c"}
{"kind":5,"id":"22f5949ec55c1ed8ce97c6d7d2bac71992efccff6b5eec37d4329c5f44b79f4a","pubkey":"a104303328d41ca62add479f54d19467aaf0969ce09024e0d46dd577706c9bae","created_at":1785676829,"tags":[["e","958d7a6b0092f0d0e331b2dbb7f197504de75ba442b7fea806be2098c7c2eb2a"]],"content":"","sig":"dce8d65f9939f32c404db12b3ac29ebf01d28bea8a58346a8d886cd174e799e46d945c97d96bdc0ba4345c5ca44b6e9827cede44ac10efb931bb3f831e8935d2"}
{"kind":22455,"id":"173ac3706d5caf2f883cede799f54e93a1b89dbb4cafa1885703562208a2350a","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676829,"tags":[["x","i10614r6s6x481r3l6875fg1631u55a564j"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\",\"offer\":{\"type\":\"offer\",\"sdp\":\"214,198,243,224,5,154,106,180,224,102,15,15,79,30,115,171$ff2wqB0tK5EY9LkoZ8yHi/oyz+MfFpi/6sLjRP++zumM+33SIHLHt5lk9bEZeCZGnWaJm/xGeBLkAjrIvGLeCU4T5FZ3vuFvQUWJmo2MmdbCPISD6scgXXpVx854ExqWcJjRrTa6eFY1/2bhJZ1/9gxjPZ+gX4NoXcQpk5FbTFI9scY+tokJCGS/VWIt9on3dKA1ABXvfHUaV49hlTi2VmR1/RZsz6UEzSdz4RJ1erFZer0R12CGhJ5QAg8YTAcfbFe7D1gKQPb2RnFUMNyBEHbJNys9jPnq5sF3QoA8JnxgSZgDs0nttXfmuTtywGqY9zvCNp3kV/n0BatZUlAyzhcPgAqlkA+I6L82hT2vY6Z2uUJOYRi/yiAswm7BU8PaV5vkgViODnqANVnnOHHyDpRrMhWEKmFuenxp5cBs505WkauYI5gSIuAJCE2aWhcQs1jX5uGMU6UsqJkUv83FdEZ6Syi4EEVxT9waIZmBuNE6XOKn2WG7DhY56FZoquzH9b+Nh1yeJiYkBldP5AMQhmzUHK0oP0iF1EfmxW8D6sJEWYhR+3mNkOF+cITYdVbwotN6Ijpitj07yP7/QtzeaWENsEJ4hEz9vODuSkgiFVNX8bwSjwHoreD6O/71DSsHve5WUe8Tld9K4xMv7+Qwe1pnbLea3F8qxEfBAnJVVpVXUZ1u97NuO4bsQ1TduEH+ie2cFE3MKZq16IIz3+8+fjMa0DO0UrxaBa4dX1au6Yxgd0EV42gCEu6uGqSnf6VzTjBwMoGdQsDK4PswZ8M6052ik7MBBYbq18VMm0eqBfhN19R2IFB6s9k7P20gzQYd+r4no4ynERuOyJrGKD9RrSJ8L9Ta8n+5ssFcHOFrFAehxoD82XWUtv2tviwLvjKUlU0ocvbiHAOssJ60cFha0Bt+zJqlPjhjH93MciW6Sk3FeaFsFZteilwQI1sn0f3rmk5p8xXFG7An21gswdcCEXnssKxLi5dQ5CsBD3+qz7sNzxHALi2KnVWZbqhi347XZ8HMcXeGE3HM+bKkKJ/RkJ6c0ofhqAwp5ign3Njtey6hLkFKz6sF9Oh6x6e80ec58nAsIQxRApzLgojJ5lUKVCb7dyhtJpkeGrCNVuVqiwe5dttQVNeWk8HHsbBznLF65OKy7tJ9CjVRibeybSLpNNZPDyFk2H4UGC1jSbwRWQM9kymWRruuIGrUdLLtvHtbakA+RVDItKHh+tuchA3ghtsBw+0x1YTzNg/uewokU6Ts6g1xcYn34PKCuoEbAm7B0xijw8HRaUA7/1lSb6XupDcddAMsdXDR\"}}","sig":"b4df17fe73796261f380dbf59cd4c253691f7df7c5ed02ea7ce17ff08ece4e1244ae692d384577c3d5a4b3a52ed1823f4e21a3b709fb8228966e01fd9cd96446"}
{"kind":22795,"id":"51840075a161979e0960dee2cf985dcff29ce8024542224f1e8d270f05d266dc","pubkey":"146c70d40e1f4f96528309629258f8dc97e9a61aa53e54e13b999ba5d2276968","created_at":1785676829,"tags":[["x","2t6r5t3g2w4ke3c5q5fe155b4733m3m4d204h"]],"content":"{\"peerId\":\"9IVDCiG4hlib9t5iG017\",\"offer\":{\"type\":\"offer\",\"sdp\":\"223,148,124,74,154,14,67,150,164,85,250,22,207,70,113,66$GQ31axJJQOIow+lfqV1YKLNxUh+V1rAioseBNHgVEhYONSeICwYKTUGHaVGQjflxRs1atY42oGidpCqpxjDXdpUgIQb3/EzXDCEydaqqyLy5l3m25dy5k7ufZFX15VcXR8wcDi0RYRFN/iGE9mSHsLV5w6lQqv3jpTsKAeFYqwCkvzh5WnUwM8PvmR+PYFTXbb3HXYwJp76QGR3POTyJxvijjtjkP0Or3V1JrPLxMDJIeNlNjWi4r5CzFzO79ymTE2/5b2IBlN5FaZbTYxRvICaKVA42gztpDDUFUaOfsS74xCun+wQfnwf4cefm7h8HochY45m/43oZ+B69kHmQlXdzRDtxPyW4Nlhakp4+fpvMuaO+AbAjLuJeKxOXbBdheXjQ8WBJyJU6xXgDmUy/x8cSJtMfshnAR86ENlKSe5TNR4ZGEkDGt8ZibqtHzr3U0lUFhfCNTnqdGhPKPFCr89W6ifbWkY6Ap+8DsiqzBZdS9Jm/rj6dBpADWXu1eCH95TNRUhA344wAGCdLITq+5HFEG3U1+F9e9Kv9anp+QXFRo1DSs9sBdKIzH2GOoYgvv/amzP0t/ujF0jVC50YbJ+vcD2Mk738T/RoSw6fenLHR+vuSo5GEQZKfIqsDnLol9MuHwilistA/EiTl+g/5oVoZfFz9Ka+paRsyE3jVx/r9q0/XqGqCIVGPuq8Zq8Q9upXxHZJ6GDWWDnkbW/Jdh7lzkPEgPk8A6mhv6tKVo9yKPGQvcRC6ir65pvnpr8LkcmGD0ZcmyUJZgcS7QYtHdFr5nEm97uOkDEcjWNsEY4BGuZLquE5YXA/FEKT9OfF9IGANGeFCejUxYC6wmfBgNKPHn4y8dwF4O1VS2dyzvIEMeQOWZfLX18Rfzwer2DUqhy9Vug0UlH85Zg7BO3xsd7HckWkGhaQe9/OoYc/J7qu7kgGGaJEot4tkVx1jqodqfQ8231pFSFYAPzqO7ARdk5Iah+4o5leT7/3etL+NGI0P5NRngirLDwDf3usWIoP8rZHH3X80VvUdSaXfNNVIfGtVtVEoldpJdLMgBjjrZN4bSfw0Vrxq8ARXPF1XhziTKhhUknepY9kinMynYwebwmr9jxFJhFOvrh40Pm1SNAEegoMxrBoz/BfSUuFlo4ZUOdT4H29npvGYrQQ6icPfu3ksOI/a1jEzIlf3Mi97gW3q+rVj4vNVx3PoqOi6Ndbp/vSqa3cMNJXXPfTQ0V8w+4SW/PwyVqnhIl01kqxjNdtdis4EMQ4ceGJ3MDbgCdLA6lG6tGvogy4V9/esn71GfC/fD1Wz\"}}","sig":"88644bb037cd5b75c311efc06f45f39c6e8100615abcc08ed0f285ec9e282733a88838369e318af75e2687b7bdd6d0904a4bb90fc2f16027755d3d060f5e30e7"}
{"kind":38501,"id":"bc1428364c22cadcdda9e97f47a30dd3da07d32bef447c987e007e6cf33b7cbb","pubkey":"8b46fa734a7230497197cb033e6f10e55fa400e90447cfe5651c9ad30a9f7b4a","created_at":1785676829,"tags":[["d","btf-descriptor-2"]],"content":"{\"created\":1785676829,\"enc\":\"97f504ab5a031b93d29a813de02ccbec80f0345b11ade65b66cc0f89c8470920\",\"meeting_node\":\"90.156.222.107:8434\",\"pubkey\":\"8b46fa734a7230497197cb033e6f10e55fa400e90447cfe5651c9ad30a9f7b4a\",\"sig\":\"eb1094bc2e5aa1c3d49bb21f9ccd24b96a8066e1efe7400d649c97a03c7e599b1f5c820dd700a3502b675c52513bd0f2bea34a4dc41fd4c101dc621e4971378c\",\"v\":2}","sig":"2053e323b44d3fdff3f956edf9a61ceb3651ee517eea4bf11adbe7e4438fe63234aa7304ed3cf5e13455ef6037576e89c6587a771e514540624b2f4157531e27"}
{"kind":20001,"id":"6c2fac4c1a3d24f3c83db08fd810cbdb92e47b48b9b0bb3e54ff7081882250f5","pubkey":"bf5ceea34cb45bd0d4903e83c62a49ba1c80d589934d83fd4b2c616aa7c3df57","created_at":1785676829,"tags":[["g","u3"]],"content":"","sig":"ad150f48ecf1ccfb096cdcde24a846a8396d1f8586e238a778916836c78a56752070c2833ec655350d9241cabb4785dc4e48501dba5ee1b35ee442b80f677de5"}
{"kind":7,"id":"c138c6daa6626d75a2621f1c15729f63abbc682773bbfb278412b6545131ac5f","pubkey":"e4146d1d7fb2f7f6e6b3c799eaeaba6f7446fd28ccea2592671cb239a13f4d81","created_at":1785676829,"tags":[["e","f148576d2a0f9754d8006ea10f31531c600ca6abbbb64f92b13146ab32316e12"],["p","c79200d36c345e41d46c135b30077b2f15d43cb286069f1e4f2e65c09162a900"]],"content":"+","sig":"f02914b8b9345f45f4ee7cbdf7e058e3a15c976436df11b71ebbbb7f62d08bfbb8398cfbd202095bf9dd3d4bee6ad597d9289d12c8bf32a8b57323666ccf5028"}
{"kind":5,"id":"05a69cae2f6f03f85d49ce912ffb234405ebd40b4619afd0f48e79f4c9afaf8a","pubkey":"3b6b4b29eb5b65aedb5d5291ed382d64dd62c555911380bd0dd27f160446a025","created_at":1785676828,"tags":[["e","64b710cf656b37c7d2df8935752af4914b88f8bb1ce3536c1315da43b72b6820"]],"content":"","sig":"f62372888a6e0ecc9b865ea00c18425f46984212a929441def9ca841e7216f95e4ceaa6eb39e25dc8a533d3455dbc1191450c053e894cd7a6a752b3026694d86"}
{"kind":22850,"id":"d502724e18c11af192ee65a8798828a5839de91251fb80dde196e09751600622","pubkey":"972698b75bab763abf02e914c05ab10ba92946d949e44994914bc8ec0f0c8356","created_at":1785676828,"tags":[["x","154a6xc4s1134333k6j1w3i5z2u4x2g27363w6q"]],"content":"{\"peerId\":\"E4BBrrzMaIncSQjB5BIY\"}","sig":"ce077337ae276256eb927f006fe5ebd9062272852330909b8e372176ac17e9cde6c75f95a91a8a31971fd50e39d3f09bb78a8469444feaf934e220ffe295b1cf"}
{"kind":21059,"id":"c70dc8831fdbe3c9e881ec501b0164baad462fb4cdf966b232771faa15faf8d1","pubkey":"f6618de562d05fc1544e473cd2de7d19172886a84413f09be3580091b0c6166c","created_at":1785676828,"tags":[["p","d71691e02c10d3564530a86b51598532eef5f3d62317ff93cea2c3b9c4d4526a"],["expiration","1785676949"]],"content":"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","sig":"15cb4aa077947993d94481c3ec275fb4905a66a0b0309ed2962170c104900322ef9cebdfb10c28f55e91d2ea1c5ab66682d570f8146db4b1e35ce71eb465ba15"}
{"kind":5,"id":"4465287c1593ab85700231ff2bdb75b84cdcd7f1110333bb4d615fad2d73b147","pubkey":"fde8ca9b4a9d1f5d2ed8b82ea13766c37bdbf6cb50bd272d642eaeacc713563d","created_at":1785676828,"tags":[["e","5982eb4b4dc892cdaeb1a37d3329c3b11633be2b19401d1e0f2881309be0616b"],["e","64b710cf656b37c7d2df8935752af4914b88f8bb1ce3536c1315da43b72b6820"]],"content":"","sig":"806cc8d8fb44a1b2fddc62950e525f42bcc3dd184ded730235b0c4bc36c13017e86a512f98a3d005c20affd8a546fcdb99ec3c90e4550a47e1dc2034cd86cb16"}
{"kind":21059,"id":"638a053daeb7e0a7a0f36a2e2b1c0e2e36d25c58142139f99f706244fc7c09d8","pubkey":"1a54ab43a4bdcf983de90f5bcb7ec25cb61b7cee8a5a719faa7c7f0abdbbc88b","created_at":1785676828,"tags":[["p","92e744f3d9e8ca5888da31ac871ef9eed2a0b198ba08f417937cbff3030b0000"],["expiration","1785676948"]],"content":"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","sig":"134ad847b73bdec6eb45c66e2971175bedd00fb7a6c1a6c708beaa65fe8d4089fdb58e3c347631cb298e209df1a5183a0b84513e271bb8fd1f7a20696da68c66"}
{"kind":22668,"id":"ddc1eb28e2625a90f93416b40326661d5f5b627056519d83e58625ea117a5a67","pubkey":"645d864f8e4df2e7961010c726ca1dfee79c1f849e89981f38c67954ab27ab44","created_at":1785676828,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"XLa4RppouLPlwZlPEN1L\"}","sig":"9bf8af313f3c5596cb8f4d0696b8d72cd268af14bfaa38b831caafe1248eb58a40af81fb3a43fadd6491b086f811f18fa6c4422548ec58c37fd3d157f4bf8eb3"}
{"kind":5,"id":"0b34123c6c388de6d35d821348dcc9651adb2eec495f020f9bf29e32a9ee5282","pubkey":"a6cd35843a1471debb1b174b5bab472d9f5a2e2391e92207c2279468c3e9ac9d","created_at":1785676828,"tags":[["e","8290a6025d0f18c48f30f52570181ff53bb29b44cb2af6234857b7792650cc17"]],"content":"","sig":"a00555524925631aa1e50be3f5bb87518191259643f46806d55e13a5c791a3f073eb82e8e442b0979f269eaf323c846b8d3e0bb6185f622c1f734eeb444c4216"}
{"kind":5,"id":"290ae3e79db0ee20be75bb4368fcd1ca3e4e97fce13390c135f5a00e24daab6d","pubkey":"f562b3ec661872d7e53a9babc2e75aefcc3d1d658aa32070d7a6e3948dab2af2","created_at":1785676828,"tags":[["e","c75edbfe2657b06d905d775d476618bc97a68398447ed36b5b53e381ff34b99b"]],"content":"","sig":"ad8cb04b8a8d861e079d122c5e459fb9d505a4e024fca20d606df2cb0cd5a5701fff566d0b52e22d978e76eb2bfa8b115a207a1de074dbb307bb8b8c1b0bb614"}
{"kind":5,"id":"da568c14b08dd42c0455f01d8239aade88c77ce1994de16260dd198b308a3421","pubkey":"b454400c0f61af322680e46825a10c69aaa4226be2f474dc21fab2c7271d4ddd","created_at":1785676828,"tags":[["e","098b11ac3a658a5f260afe741b1e702b6ceb795073d0fd67f2b39893c9f5f1a5"]],"content":"","sig":"860316ca6bfbf5032c9086ae56d89b65085e4adb6c083f0f2a5b369cdf4951673b4a4dbd565190b071d12681f11e55495b3a354a41b85a19f68c064d3f010bc8"}
{"kind":22668,"id":"2e19b89f9daadb9436e59f893fbe4cfb06d233052bb1aa2e8e2a5f2ded14bd1a","pubkey":"983451ee5aa44cfd054418a4b4d61389f6cacfe5bc8ad63f247040506a7bc8f1","created_at":1785676828,"tags":[["x","3h4b5840o6s3o6aq23294k3l3u32544u3s7266"]],"content":"{\"peerId\":\"MOfdN8BFCzSXK2ClbLZp\"}","sig":"7a7fa69ff865ca2548336fe3bb0c8fc72033822efc524f28f0056654fd8f70ddf0c4d0bf6266bcea2d320204fd7e87b9304ebfddbbb46dd528816e7a5adbd93d"}
{"kind":21059,"id":"c76184eeacfdd807619fc4cfea4d898e87cd44a25b4f534f22f4de068974a659","pubkey":"756284430945b4e12636df19fb8892364d6b685f15724c3cd4cf8089aebce8e8","created_at":1785676828,"tags":[["p","c3d2d513091f404497181534e7965d2b80da38f82ac76bdc8faa2ee9b20b7fe8"],["expiration","1785676948"]],"content":"Am3Gnyopram3SuuX4K+cN38YK5olmSc/2iHfSedpdzflQvTfC/3EuJejI9Rm1Uf0usuy7zeOXezGhkqAOPLmCJDAbbBRAzhDD1BX9QRRsZJud/4U19VwQ9fnxCA404DzK+sO2nOu1GbPjG07XX+FRtjxjNO1I5k0iYV8f9hECdUUJ0UmW57KMJ+mr/DjP6Drj3I30KqVxqCLxxcIpgspY49FKCgqvWnOj7cKQroUJFL0BrbpXX+4Vi7d2AzeXU6EJD34PwrUwxNZ0QWxxM74bUsoDpO7pkyEzTdZ2IgkF7HTRV5YxViuiiWiisbkfDSjAR8m6V5d4jc4cQl3NxTgNbDPmTOe17j1+7dmZgnm/9AuaNW724Wrc2A0I6aRK0iWNerVDku9RjYTCUDFUQIXWydTX0nGR9lGMgJ4LmSDM+F9pjOL4KWAt+4dx54pB58wZS8Rr43kfW2nMua58ZoupgYKnEo+iNOK1jx2cUq9kzfJwVwj97I7fr+qnQ8Om0+YKCVbqk1gXwPnAY4K0+pinYTC58J8PZhRQI0uXShOo6MfdtHfEfEfDdyMu//GpU4PFNfXif51T0JKYsDqU4EBpmMRcRCdDpMEw/hPstB5lKHwtZhSqbeQr5mkh112aiBmbiMXGPRGzxdzGbxJjywiv7kakFlZurIZqMb8lO3t9oYuvKXvZ3msoltPNn+x/aNVF6DMPcff4QPEkvDP9wwBsAjFSz3Vez7amp+GYu8s7O4zk/Hk7Tpx8tNBEhQfSwXcMFkWINctrIg0LJjrz4T35G26kTR3YThBh0EmABXUxG4TpdKkeg+4f1V2SI03kTHYLSH4tXSKlktAJXj/+Ia8LqQhLN7cJ6WAvJxHkchWqREdOZEH8KJ3kPgfC201tBSAEtIW0ZIs0PB2qNKek2K8oBIUkdg2EtuS4H8cJMOkUM3VHBl53oiTLxR2ZRrLcWaxJj99IGOgMEekIvJCMk6HsL8tCu/ZPch3HvOyCssyTeB4pH1o+9xtbQbuvu6Zbu07NpN3oqoKai+qwUDF7SSvO6qHGCP8fM93w8k3sF1laHf7Xmf1ojGh4z1y75P4x5PNrA5uW7ZJ4NLEFgDW9dECKTzXT8WDk2OAoR4rc6AVBFOCXAnMgMEDePf3qJ2d7OP1KxhjSXhgDRuHJ434vEzvdZv9WPexY+TAiU/0sWW6aobINhaz/vx2Y9nuUqipnrF8RSUOsp+GyurceN0TrI8u3Fj+M82Bs2py/xhogoSVxIsMSpNLWo7MsCXfoq185No6s6OyPCbYdQX5Dawi9fHheJ5wUWNO3fuydD4qZVqbkQEBgaQV1KvCDHyy/+JAP236GtayULgenOT26xy3dIgkzrM/gz2cgyuCLZpDNnQRsK69bAYpcy1+ZZaHh3+v0nk9Yj9J81CBLrBKeTMW7bKozIrwV/pJVQD3BsbW51gEJNaNfI473sluoLcPbrn+ef+znENPONMANDwwl+EoOBxeN5+TjAaKJUWnsPkxETFSa4vFMhErAKphtHmbh/OcsKsMyriglQKHpK1zRcxcmc3enhKFJc3IWPl3b6aUGZ8keueYpxhDngfMih+qmsIlw8/mirCFc/fk1TxXuB248h6ZFfZUD+dE7KcNlQ6QXkys8EaHG/rkBQ7X3Pp1qh5tEY4aYmmgUqHbt6vmM1g8qafd10hPSEE0r6amcBu4tKY3FEx9IH8jyjottQuFEeWHGoS/2XlOu1HduOKV4wNBosFRMIZdvDTs5gJ+DJxSHZfoA6dBBrKrm84Vf+FanN+smQDrmF3zc98ddeUC9kCfy4US+qOUxFIXKYp9mWZjelQKlK2ioN4SPiwY03Nn7dfPRu77hnXMoupvy30cIFPQf52o7Pqebqn98HO7TcwcP8kHyZW839JrMZ4EOHyJQhPw4BMT4HnOxRlq2Et8TEvExzazq7r9Ky6KZB6KNOqpxuhuo0IkYiT0Nshi4J078UUbfrLOlt61ommvGSqpRHqr+Yr8Up6VsPZ1HXb+2DnXPW73/l95bhSnuo02nheJicXfPC66bic7mpLcJPgTz68LLn2qXVhijMcy3r0uh7gpiKLLUExYWJE0rr526ihhuuypbEfoGzzdZJXi9bQXX2y1rnqAJ0ZaQjHtk1CYUyXkNIIzSDkhuxC/NRfKPUy1+M5p74KmUEMAvGuoQbRmV50PGyz8BJa9S77rfo/qnJStYDP8HJOwFjRP1myIZk9ks15uRd85IHvOkv33tVuUSe1eCz1a+naYLPbJFc3zOi/gBIvENA7u2CxLJ/bcoE3YuPtUWFIu/9dZKt8XSM0UvlCJ6gLTiZ/ZLIbByo0bOOeqEdDNMcwPhSqMtLlH8kNWzGqPJIOPQ/lrbuMOKiJmz33PqQw+K3l7E7JxXsIsDBwtkD8GHMgChVd4G+r25qiNasoP86QLp46O3GqwRoJaLnhPoqZUiyStkL1DZ39lgasqxdNhMKJWY+bnbj5XpzXRyYiebxGHVIgpxebRktxWnlWkp8TknjC0bhKxX85XzpI5yg2iSe8GyKvCy4Wie2VAh43CtrpJAwhKThesyLZX1yuFqgTlgQ8u78ZjWzHkm9GU/yuOkfbIExgMhoe075hFqKjV9enCqe8Fo7JqsYC1WbePfAnEsYTe7leDnK91fidQKjtl/AEAeYttXlRbmilWVqFvITqMu6l8sPBWcAdPjYwQw3Op87Tz36O3tIojvLW4veOQR54ViGKxCJqPmqEiZZ1juDVDgRYQSIRCSqRDeqrIxpXYE9j/7xGtuvR3JdcIKO4WfQkTF15nuOELBxmHn+yQIF+6WFVB","sig":"8a7c9e47f5e301802cb6eddf0c11109a161c7873aebfc4bdfb6660dd4da3d772d4c9f13ed6708a6726770a19a931b0d912ad7ef9b2289e1471e0503b64a9de9c"}
{"kind":22734,"id":"c4eb84f16a0b91da08b80674809ff770cf2fbbbea535222f71a6f70b1afaae2f","pubkey":"62131eedf03e285f05e7327cd868d6cf04567658e72f09f6a027a2cb526491b2","created_at":1785676828,"tags":[["x","2n2c211h5a65204bmik1t4y663e1k2jq1j2w"]],"content":"{\"peerId\":\"5JyB3m4D09Af6swaeL6g\"}","sig":"b025b0f3d0f2d5de0256a1401816a8393a3109bdb2446b4a4986fde228438a1837a54a3b0d9debda4528c9e840e6acd458daa7d7e88d45c735ebe0149868160d"}
{"kind":21059,"id":"f8bb284d3b89bf6edc85ac8157f4fe146b1fe1201164a094e85dfdd6bc9004e5","pubkey":"8a67ab6ff23ea573d894131bbee2f3b49dddd2cbbe9c532b0ea8bab5dde6c27e","created_at":1785676828,"tags":[["p","2b794b141e7490a4f53e23d41286cecdecdc1bba9aac19dc92ddc3984d361992"],["expiration","1785676948"]],"content":"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","sig":"726a670d5ca1bd6ce671f3c34ec0f4102cf8a9253380154b0621849290fe856b805fb69b772e55cee187879f4a4759e044e679c03455e513764ec7dbc50bdcf5"}
{"kind":22734,"id":"69a3ed5c758ed45a82812314ef3dac047107069f56f1f938dc6e26a0d87ea86e","pubkey":"fe577acc08542c36add5fd831b6ca9e248d98f2a24f9648ec71f42947e289710","created_at":1785676828,"tags":[["x","2n2c211h5a65204bmik1t4y663e1k2jq1j2w"]],"content":"{\"peerId\":\"FjdIMX0nehvvfKeE8uS3\"}","sig":"8c52eb4118de1f91350941bd08b6953d6681afce8158c4fdf2da1addf2e15e07454a671bfb8b5600bccb4544380b3f2ff49161d22330a1138943781392a1ff75"}
{"kind":38501,"id":"8bb6cd8e927c497c1d0d43380fae119b3e1de4bbed9df532ca8c85b956508b14","pubkey":"be43349f0292354b71e5857cd3ef5577e4d8882b50d5853b8d0d2e6f33cb9b17","created_at":1785676828,"tags":[["d","btf-descriptor-2"]],"content":"{\"created\":1785676828,\"enc\":\"ae5076051e63594c2bbaca4fbd1dfdeb661276f7bfc325f7b6e6e227068e6248\",\"meeting_node\":\"90.156.222.107:8434\",\"pubkey\":\"be43349f0292354b71e5857cd3ef5577e4d8882b50d5853b8d0d2e6f33cb9b17\",\"sig\":\"9199cc8116a79d3de74e28e1171a5f88f8ba0d3ee58fd620a9bc06b63652f85b9b03e583b4a3f5960a2d56a1f4d9c3eb2731def42d75bd28ee6ed7c65609b4a1\",\"v\":2}","sig":"de12e3a0b7ca656aad163076e2f32e5de2878a55b163e7ddad075e6e8ae9d98d4aac1502bb76f69c03086d4e7350f5d32686dba1295788ceb9dfd4049cc657f2"}
{"kind":24242,"id":"967ecb7f628c4c40e6142b01d1e4fcfc021af09ceffabdf3e6e5616acebbba13","pubkey":"a975b6d0b2376a560d115745432db9828936b586e85baab34b0faedc1ec69cc9","created_at":1785676828,"tags":[["r","5ae4bcc88c92069957e24564ee8f8f0bbc50aa0ec33740fa6df513c5b4261c58"],["t","P"]],"content":"{\"t\": \"P\", \"d\": \"i22abIUK979JGbf+XiW0SEkynRl3oeD+ub6fdvXs9e1HiuSYhHzExoBu9kIDplEXnBZLQ5GckxdvfPVMCI5wLRruGhCQ8FyJxZMN3BOlkS7ExKGTXSCX7ckXAro=\"}","sig":"9ae34fa8b69db93c5469d472d3fe3e357370412926cbbd161c1c65ddf8a01531b0cbd184f46531169d738dc97f9ad647a8ad0eb0b8f768bc1bd05b21e424e086"}
{"kind":10011,"id":"9fc9a8ddbcea381b42dc97807b9eda3e22ef405d389804ad1ed86e2c72f39534","pubkey":"5de310432a95a13ba1905b5c78b511b931fa7efa86bea15057ebe16424cb9273","created_at":1785676828,"tags":[["t","relay-monitor"]],"content":"[relay-monitor] health check at 2026-08-02T13:20:28.031870+00:00","sig":"aa3d24c702a8172c6a28e59ab84ef4e831c7ebe2153f19dea71f92cdb8d4c35495669bc5eb80df2c9c7eea273d919fbce1e26fc226ab22cd4cfa202aae6e0aef"}
{"kind":5,"id":"a9a5a2840cdedd585ef53eb3fb086119b09d6ec97614a19e7014edc8e9fbfdd8","pubkey":"abb85588adc1f9fc70c07c2d6119c7ea8483e05674d85e89fd12fe26f37d7b80","created_at":1785676828,"tags":[["e","6baaf169c5a412c48bc05796ae31420fbf0abf7f4425030a02a5b5fb6e89095f"],["p","abb85588adc1f9fc70c07c2d6119c7ea8483e05674d85e89fd12fe26f37d7b80"],["k","7"],["client","Amethyst"]],"content":"","sig":"49e4211186e0ba933c513078fad6bc0e40c496573adef997be03e343ceff2dc309616a22f44b110ae2d7ce840f969140ac83dd764d4afb0c7e52fcd47442aeeb"}
{"kind":24133,"id":"65468cf8859c2651655aef77fc73e162475671a1b9fe78507a75e8e1b0fce9c7","pubkey":"adc534e19063daed4a5fcffafcabac5c69ea92781fb870b800ada248494388e8","created_at":1785676827,"tags":[["p","8bf629b3d519a0f8a8390137a445c0eb2f5f2b4a8ed71151de898051e8006f13"]],"content":"AnvGOKiC2qxlzPRhokNkl7/Mv18NnRtNUDK3VpuOS8rGYCpUPhpICdZpq+OUZWQ12gFqaWNWEinqLyR8FXRCGKxeJ5iArhWFMF2+IpFe4mCj3Kg/bdu02V3waBzg0T4rAOaRWqsWQBSUz8ENTaeJUUhAyx11INFOWvh4MEh4ablqPY1ONZrMlfOrK5h/rqNQ0b9zbbzoeocfSUKrblTU/TcAa7bm8EnCOGjkyvfkW0PHbIND12NZG/zOBmvQIUTOkCkC","sig":"2e56dfe53f064ddcd45d09ef9211fb7d685cec414fd75537504a023be2fb8ad13b7c2ff34117e1b467902ec7fb207bd5bf17f28f83569ecbbe724e484a560767"}
{"kind":20001,"id":"828803cdb45f3fe3f1d3ad30cec52a0a608cd44aa575eb61e4781236d206dac6","pubkey":"e1ef740bbab4a0d7fa489084a5dc41bd3c829bfa80cacbd2c3fe104ef41b258c","created_at":1785676827,"tags":[["g","u2sh7"]],"content":"","sig":"e3b6b73b3d9583c71795ed5d9a73375cca25202a48f9083c6e2fd001951ab60fbdb03f30ae4958570dfb62999d8c776d5cbe6e3f0278591873ebabf0e260d40e"}
{"kind":21059,"id":"add2aaee09d5b4616d2688c3c1acd0c093f8f3e609676adb8433e7a4802b3d80","pubkey":"362e1183392e4916b8c309fd22d11b54ff7f50fb31f775c4ed7cb6c6da4cb9c4","created_at":1785676827,"tags":[["p","904d65c28c1bc185277f9e60de40e9044650b0c59e2729a4458e3c4ab6ba4efc"],["expiration","1785676947"]],"content":"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","sig":"0b358a00881890d1170b455ad5f23800a8620026939af6e98fa1d12bf5ac052b57347ce0cb15acf46c2b800be9a1e4f4ea331ef3a6186920db69fc9c43c0172b"}
{"kind":5,"id":"8cb0ebc4d6307a3df82ac8e660e4ef9e7988cea03fde286f21277ab17e67991f","pubkey":"0a4f177b087b133099e52e5b50456f0d4c832db60c0f35d3269a6a055f25522a","created_at":1785676827,"tags":[["e","ad604a106024742fe765459c43b7f45f2c7cc3123454d4d517d34c3c539686f4"]],"content":"","sig":"217950b519bd5343ca498e48b4b04637ce09ec405d211913d85201bdc76561d61b6628424ad33dfb607c0150fe63b5197230b66991ce011b8dea3734cba0b66b"}
{"kind":21059,"id":"c3d841341e03f0f22b2e6e7cd0811954f7590ff69c785965c2afe1e91cc6558a","pubkey":"608696506aca54466d337ef6dccb9cc714dd94ca1f88e91939fabec7d3114024","created_at":1785676827,"tags":[["p","08e99116ad58c5aa5e6e7dc40668d5d7f2c080fc9fd697ec0aeb76880cf54bd9"],["expiration","1785676947"]],"content":"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","sig":"887844fc5f7ce096fe528ed0a1891992556d30ae6f3e9a06177ce638f2a0dc21433678ad30483ddef40584048986d2d0cc3090975b9c96e13d8ad163b5aafbff"}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+774
View File
@@ -0,0 +1,774 @@
/*
* cache_all_test - feasibility test for "cache everything" approach.
*
* Connects to configured relays, subscribes to ALL events (no filter),
* logs verbatim relay responses, tracks kind distribution and event sizes.
*
* Usage:
* ./build/cache_all_test -c cache_all_test_config.jsonc -t 300 -d 3
*
* Output files:
* cache_all_test_raw_relay_<ts>.log - verbatim relay responses
* cache_all_test_report_<ts>.txt - summary report
* cache_all_test_stats_<ts>.json - machine-readable stats
*/
#define _GNU_SOURCE
#include "cache_all_test.h"
#include "config.h"
#include "state.h"
#include "debug.h"
#include "main.h"
#include "../nostr_core_lib/nostr_core/nostr_core.h"
#include "../nostr_core_lib/nostr_core/nostr_log.h"
#include "../nostr_core_lib/cjson/cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>
/* ------------------------------------------------------------------ */
/* Forward nostr_core_lib logging */
/* ------------------------------------------------------------------ */
static void nostr_log_forwarder(int level, const char *component,
const char *message, void *user_data) {
(void)user_data;
if (level >= 5) {
DEBUG_TRACE("[nostr:%s] %s", component ? component : "?", message ? message : "");
} else if (level >= 4) {
DEBUG_LOG("[nostr:%s] %s", component ? component : "?", message ? message : "");
} else if (level >= 3) {
DEBUG_INFO("[nostr:%s] %s", component ? component : "?", message ? message : "");
} else if (level >= 2) {
DEBUG_WARN("[nostr:%s] %s", component ? component : "?", message ? message : "");
} else {
DEBUG_ERROR("[nostr:%s] %s", component ? component : "?", message ? message : "");
}
}
/* ------------------------------------------------------------------ */
/* Shutdown flag */
/* ------------------------------------------------------------------ */
static volatile sig_atomic_t g_shutdown = 0;
static void on_signal(int sig) {
if (sig == SIGINT || sig == SIGTERM) g_shutdown = 1;
}
/* ------------------------------------------------------------------ */
/* Per-relay stats */
/* ------------------------------------------------------------------ */
#define MAX_RELAYS 64
#define MAX_KIND_TRACK 256
typedef struct {
char url[256];
long events_received;
long eose_count;
long closed_count;
long notice_count;
long error_count;
long disconnect_count;
long reconnect_count;
int is_connected;
} relay_stats_t;
typedef struct {
int kind;
long count;
long total_bytes;
long max_bytes;
} kind_stat_t;
/* ------------------------------------------------------------------ */
/* Global test state */
/* ------------------------------------------------------------------ */
typedef struct {
relay_stats_t relays[MAX_RELAYS];
int relay_count;
kind_stat_t kinds[MAX_KIND_TRACK];
int kind_count;
long total_events;
long total_bytes;
long max_event_bytes;
int max_event_kind;
char pubkeys_seen[65536][65];
int pubkey_count;
FILE *raw_log;
char raw_log_path[1024];
time_t start_time;
long duration_seconds;
time_t last_summary_log;
cr_seen_ring_t seen;
} test_state_t;
static test_state_t g_state;
/* ------------------------------------------------------------------ */
/* Stats helpers */
/* ------------------------------------------------------------------ */
static int find_or_add_relay(const char *url) {
for (int i = 0; i < g_state.relay_count; i++) {
if (strcmp(g_state.relays[i].url, url) == 0) return i;
}
if (g_state.relay_count >= MAX_RELAYS) return -1;
int idx = g_state.relay_count++;
strncpy(g_state.relays[idx].url, url, sizeof(g_state.relays[idx].url) - 1);
return idx;
}
static int find_or_add_kind(int kind) {
for (int i = 0; i < g_state.kind_count; i++) {
if (g_state.kinds[i].kind == kind) return i;
}
if (g_state.kind_count >= MAX_KIND_TRACK) return -1;
int idx = g_state.kind_count++;
g_state.kinds[idx].kind = kind;
g_state.kinds[idx].count = 0;
g_state.kinds[idx].total_bytes = 0;
g_state.kinds[idx].max_bytes = 0;
return idx;
}
static int find_or_add_pubkey(const char *hex) {
for (int i = 0; i < g_state.pubkey_count; i++) {
if (strcmp(g_state.pubkeys_seen[i], hex) == 0) return i;
}
if (g_state.pubkey_count >= 65536) return -1;
strncpy(g_state.pubkeys_seen[g_state.pubkey_count], hex, 64);
g_state.pubkeys_seen[g_state.pubkey_count][64] = '\0';
return g_state.pubkey_count++;
}
/* ------------------------------------------------------------------ */
/* Raw relay log writing */
/* ------------------------------------------------------------------ */
static void write_raw_log(const char *relay_url, const char *fmt, ...) {
if (!g_state.raw_log) return;
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char ts[32];
strftime(ts, sizeof(ts), "%Y-%m-%d %H:%M:%S", tm_info);
fprintf(g_state.raw_log, "[%s] [RELAY] %s ", ts, relay_url ? relay_url : "?");
va_list args;
va_start(args, fmt);
vfprintf(g_state.raw_log, fmt, args);
va_end(args);
fprintf(g_state.raw_log, "\n");
fflush(g_state.raw_log);
}
/* ------------------------------------------------------------------ */
/* Subscription callbacks */
/* ------------------------------------------------------------------ */
static void on_event(cJSON *event, const char *relay_url, void *user_data) {
(void)user_data;
if (!event) return;
cJSON *id = cJSON_GetObjectItem(event, "id");
cJSON *kind = cJSON_GetObjectItem(event, "kind");
cJSON *pubkey = cJSON_GetObjectItem(event, "pubkey");
if (!id || !cJSON_IsString(id)) return;
if (!kind || !cJSON_IsNumber(kind)) return;
if (!pubkey || !cJSON_IsString(pubkey)) return;
const char *eid = cJSON_GetStringValue(id);
int kind_num = (int)cJSON_GetNumberValue(kind);
const char *pk = cJSON_GetStringValue(pubkey);
/* Dedup via seen ring. */
if (cr_seen_ring_add(&g_state.seen, eid) == 0) return;
/* Calculate JSON size. */
char *json_str = cJSON_PrintUnformatted(event);
long ev_size = json_str ? (long)strlen(json_str) : 0;
free(json_str);
/* Update relay stats. */
int ridx = relay_url ? find_or_add_relay(relay_url) : -1;
if (ridx >= 0) g_state.relays[ridx].events_received++;
/* Update kind stats. */
int kidx = find_or_add_kind(kind_num);
if (kidx >= 0) {
g_state.kinds[kidx].count++;
g_state.kinds[kidx].total_bytes += ev_size;
if (ev_size > g_state.kinds[kidx].max_bytes)
g_state.kinds[kidx].max_bytes = ev_size;
}
/* Update global stats. */
g_state.total_events++;
g_state.total_bytes += ev_size;
if (ev_size > g_state.max_event_bytes) {
g_state.max_event_bytes = ev_size;
g_state.max_event_kind = kind_num;
}
find_or_add_pubkey(pk);
/* Periodic summary log (every 1000 events). */
if (g_state.total_events % 1000 == 0) {
time_t now = time(NULL);
long elapsed = (long)(now - g_state.start_time);
double rate = elapsed > 0 ? (double)g_state.total_events / elapsed : 0;
DEBUG_INFO("[STATS] %ld events in %lds (%.1f ev/s) | "
"%d relays | %d kinds | %d pubkeys | %.1f MB",
g_state.total_events, elapsed, rate,
g_state.relay_count, g_state.kind_count,
g_state.pubkey_count,
(double)g_state.total_bytes / (1024.0 * 1024.0));
}
}
static void on_eose(cJSON **events, int event_count, void *user_data) {
(void)events; (void)event_count; (void)user_data;
/* EOSE doesn't carry a relay URL in the callback, so we just log it. */
DEBUG_INFO("[RELAY] EOSE");
write_raw_log("?", "EOSE");
}
/* ------------------------------------------------------------------ */
/* Report generation */
/* ------------------------------------------------------------------ */
static int kind_name(int kind, char *buf, size_t bufsz) {
switch (kind) {
case 0: snprintf(buf, bufsz, "Profile metadata"); return 1;
case 1: snprintf(buf, bufsz, "Text note"); return 1;
case 3: snprintf(buf, bufsz, "Follow list"); return 1;
case 4: snprintf(buf, bufsz, "Encrypted DM"); return 1;
case 5: snprintf(buf, bufsz, "Deletion"); return 1;
case 6: snprintf(buf, bufsz, "Repost"); return 1;
case 7: snprintf(buf, bufsz, "Reaction"); return 1;
case 13: snprintf(buf, bufsz, "Seal (NIP-59)"); return 1;
case 16: snprintf(buf, bufsz, "Generic repost"); return 1;
case 20: snprintf(buf, bufsz, "Picture"); return 1;
case 21: snprintf(buf, bufsz, "Video"); return 1;
case 445: snprintf(buf, bufsz, "Encrypted group msg"); return 1;
case 1059: snprintf(buf, bufsz, "Gift wrap seal"); return 1;
case 1063: snprintf(buf, bufsz, "File metadata"); return 1;
case 1111: snprintf(buf, bufsz, "Comment"); return 1;
case 1984: snprintf(buf, bufsz, "Report"); return 1;
case 9734: snprintf(buf, bufsz, "Zap request"); return 1;
case 9735: snprintf(buf, bufsz, "Zap receipt"); return 1;
case 10000: snprintf(buf, bufsz, "Mute list"); return 1;
case 10001: snprintf(buf, bufsz, "Pinned notes"); return 1;
case 10002: snprintf(buf, bufsz, "Relay list"); return 1;
case 10003: snprintf(buf, bufsz, "Bookmarks"); return 1;
case 10050: snprintf(buf, bufsz, "DM relays"); return 1;
case 13194: snprintf(buf, bufsz, "NWC info"); return 1;
case 21059: snprintf(buf, bufsz, "Gift wrap (NIP-59)"); return 1;
case 23194: snprintf(buf, bufsz, "NWC request"); return 1;
case 23195: snprintf(buf, bufsz, "NWC response"); return 1;
case 30023: snprintf(buf, bufsz, "Long-form article"); return 1;
case 30078: snprintf(buf, bufsz, "App data"); return 1;
case 30089: snprintf(buf, bufsz, "Chunked data"); return 1;
case 30315: snprintf(buf, bufsz, "User status"); return 1;
case 31989: snprintf(buf, bufsz, "App handler rec"); return 1;
case 31990: snprintf(buf, bufsz, "App handler info"); return 1;
case 34550: snprintf(buf, bufsz, "Community def"); return 1;
default:
if (kind >= 20000 && kind < 30000)
snprintf(buf, bufsz, "Ephemeral");
else if (kind >= 30000 && kind < 40000)
snprintf(buf, bufsz, "Addressable");
else
snprintf(buf, bufsz, "Unknown");
return 0;
}
}
static int write_report(void) {
char path[1024];
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char ts[32];
strftime(ts, sizeof(ts), "%Y%m%d_%H%M%S", tm_info);
snprintf(path, sizeof(path), "cache_all_test_report_%s.txt", ts);
FILE *f = fopen(path, "w");
if (!f) { DEBUG_ERROR("failed to write report"); return -1; }
long elapsed = (long)(now - g_state.start_time);
int hours = (int)(elapsed / 3600);
int mins = (int)((elapsed % 3600) / 60);
int secs = (int)(elapsed % 60);
fprintf(f, "=== Cache-All Feasibility Test Report ===\n");
fprintf(f, "Duration: %dh %dm %ds\n", hours, mins, secs);
fprintf(f, "\n=== Global Summary ===\n");
fprintf(f, "Total events: %ld\n", g_state.total_events);
fprintf(f, "Total bytes: %ld (%.2f MB)\n",
g_state.total_bytes, (double)g_state.total_bytes / (1024.0 * 1024.0));
fprintf(f, "Avg event size: %.0f bytes\n",
g_state.total_events > 0
? (double)g_state.total_bytes / g_state.total_events : 0);
fprintf(f, "Max event: %ld bytes (kind %d)\n",
g_state.max_event_bytes, g_state.max_event_kind);
fprintf(f, "Unique pubkeys: %d\n", g_state.pubkey_count);
fprintf(f, "Relays: %d\n\n", g_state.relay_count);
fprintf(f, "=== Relay Summary ===\n");
fprintf(f, "%-48s %8s %6s %6s %6s %6s %6s %s\n",
"Relay", "Events", "EOSE", "CLOSED", "NOTICE", "Err", "Disc", "Status");
for (int i = 0; i < g_state.relay_count; i++) {
relay_stats_t *r = &g_state.relays[i];
fprintf(f, "%-48s %8ld %6ld %6ld %6ld %6ld %6ld %s\n",
r->url, r->events_received, r->eose_count,
r->closed_count, r->notice_count, r->error_count,
r->disconnect_count,
r->is_connected ? "OK" : "DOWN");
}
fprintf(f, "\n");
fprintf(f, "=== Kind Distribution ===\n");
fprintf(f, "%-8s %-30s %8s %6s %14s %10s %10s\n",
"Kind", "Name", "Count", "%", "Total Bytes", "Avg", "Max");
/* Sort by count descending. */
for (int i = 0; i < g_state.kind_count; i++) {
for (int j = i + 1; j < g_state.kind_count; j++) {
if (g_state.kinds[j].count > g_state.kinds[i].count) {
kind_stat_t tmp = g_state.kinds[i];
g_state.kinds[i] = g_state.kinds[j];
g_state.kinds[j] = tmp;
}
}
}
for (int i = 0; i < g_state.kind_count; i++) {
kind_stat_t *k = &g_state.kinds[i];
double pct = g_state.total_events > 0
? (double)k->count / g_state.total_events * 100.0 : 0;
char name[64];
kind_name(k->kind, name, sizeof(name));
fprintf(f, "%-8d %-30s %8ld %5.1f%% %12ld %8.0f %8ld\n",
k->kind, name, k->count, pct,
k->total_bytes,
k->count > 0 ? (double)k->total_bytes / k->count : 0,
k->max_bytes);
}
fprintf(f, "\n");
fprintf(f, "=== Raw Relay Response Log ===\n");
if (g_state.raw_log) {
fflush(g_state.raw_log);
FILE *rl = fopen(g_state.raw_log_path, "r");
if (rl) {
char line[1024];
while (fgets(line, sizeof(line), rl)) fputs(line, f);
fclose(rl);
}
}
fprintf(f, "\n");
fclose(f);
DEBUG_INFO("report: %s", path);
return 0;
}
static int write_json_stats(void) {
char path[1024];
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char ts[32];
strftime(ts, sizeof(ts), "%Y%m%d_%H%M%S", tm_info);
snprintf(path, sizeof(path), "cache_all_test_stats_%s.json", ts);
cJSON *root = cJSON_CreateObject();
if (!root) return -1;
long elapsed = (long)(now - g_state.start_time);
cJSON_AddNumberToObject(root, "duration_seconds", elapsed);
cJSON_AddNumberToObject(root, "total_events", g_state.total_events);
cJSON_AddNumberToObject(root, "total_bytes", g_state.total_bytes);
cJSON_AddNumberToObject(root, "unique_pubkeys", g_state.pubkey_count);
cJSON_AddNumberToObject(root, "relay_count", g_state.relay_count);
cJSON *relays = cJSON_CreateArray();
for (int i = 0; i < g_state.relay_count; i++) {
relay_stats_t *r = &g_state.relays[i];
cJSON *rj = cJSON_CreateObject();
cJSON_AddStringToObject(rj, "url", r->url);
cJSON_AddNumberToObject(rj, "events", r->events_received);
cJSON_AddNumberToObject(rj, "eose", r->eose_count);
cJSON_AddNumberToObject(rj, "closed", r->closed_count);
cJSON_AddNumberToObject(rj, "notice", r->notice_count);
cJSON_AddNumberToObject(rj, "errors", r->error_count);
cJSON_AddNumberToObject(rj, "disconnects", r->disconnect_count);
cJSON_AddBoolToObject(rj, "connected", r->is_connected);
cJSON_AddItemToArray(relays, rj);
}
cJSON_AddItemToObject(root, "relays", relays);
cJSON *kinds = cJSON_CreateArray();
for (int i = 0; i < g_state.kind_count; i++) {
kind_stat_t *k = &g_state.kinds[i];
cJSON *kj = cJSON_CreateObject();
cJSON_AddNumberToObject(kj, "kind", k->kind);
cJSON_AddNumberToObject(kj, "count", k->count);
cJSON_AddNumberToObject(kj, "total_bytes", k->total_bytes);
cJSON_AddNumberToObject(kj, "max_bytes", k->max_bytes);
cJSON_AddItemToArray(kinds, kj);
}
cJSON_AddItemToObject(root, "kinds", kinds);
char *json = cJSON_Print(root);
cJSON_Delete(root);
if (!json) return -1;
FILE *f = fopen(path, "w");
if (!f) { free(json); return -1; }
fprintf(f, "%s\n", json);
fclose(f);
free(json);
DEBUG_INFO("stats: %s", path);
return 0;
}
/* ------------------------------------------------------------------ */
/* Main entry point */
/* ------------------------------------------------------------------ */
int run_cache_all_test(const char *config_path,
long duration_seconds,
int log_level) {
memset(&g_state, 0, sizeof(g_state));
g_state.duration_seconds = duration_seconds;
cr_seen_ring_init(&g_state.seen);
/* Open raw relay log. */
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char ts[32];
strftime(ts, sizeof(ts), "%Y%m%d_%H%M%S", tm_info);
snprintf(g_state.raw_log_path, sizeof(g_state.raw_log_path),
"cache_all_test_raw_relay_%s.log", ts);
g_state.raw_log = fopen(g_state.raw_log_path, "w");
if (!g_state.raw_log) {
fprintf(stderr, "ERROR: cannot create raw log '%s': %s\n",
g_state.raw_log_path, strerror(errno));
return 1;
}
fprintf(g_state.raw_log, "# Cache-All Feasibility Test - Raw Relay Log\n");
fprintf(g_state.raw_log, "# Started: %s", ctime(&now));
fprintf(g_state.raw_log, "# Config: %s\n", config_path ? config_path : "(none)");
fprintf(g_state.raw_log, "# Duration: %ld seconds\n", duration_seconds);
fflush(g_state.raw_log);
debug_init(log_level);
DEBUG_INFO("cache_all_test starting (config=%s, duration=%lds, loglevel=%d)",
config_path ? config_path : "(none)", duration_seconds, log_level);
nostr_set_log_callback(nostr_log_forwarder, NULL);
nostr_set_log_level((nostr_log_level_t)log_level);
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = on_signal;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
signal(SIGPIPE, SIG_IGN);
if (nostr_crypto_init() != NOSTR_SUCCESS) {
fprintf(stderr, "ERROR: nostr_crypto_init failed\n");
fclose(g_state.raw_log);
return 1;
}
/* Load config. */
cr_config_t cfg;
if (cr_config_load(&cfg, config_path) != 0) {
fprintf(stderr, "ERROR: failed to load config\n");
fclose(g_state.raw_log);
nostr_crypto_cleanup();
return 1;
}
/* Create upstream pool with custom reconnect config.
* Use 25s ping interval to stay ahead of Primal's 30s idle timeout. */
nostr_pool_reconnect_config_t reconnect_cfg = {
.enable_auto_reconnect = 1,
.max_reconnect_attempts = 10,
.initial_reconnect_delay_ms = 1000,
.max_reconnect_delay_ms = 30000,
.reconnect_backoff_multiplier = 2,
.reconnect_reset_stability_seconds = 30,
.ping_interval_seconds = 25,
.pong_timeout_seconds = 10
};
nostr_relay_pool_t *upstream = nostr_relay_pool_create(&reconnect_cfg);
if (!upstream) {
fprintf(stderr, "ERROR: failed to create pool\n");
fclose(g_state.raw_log);
cr_config_free(&cfg);
nostr_crypto_cleanup();
return 1;
}
for (int i = 0; i < cfg.upstream_count; i++) {
if (nostr_relay_pool_add_relay(upstream, cfg.upstream_relays[i])
== NOSTR_SUCCESS) {
DEBUG_INFO("added relay: %s", cfg.upstream_relays[i]);
find_or_add_relay(cfg.upstream_relays[i]);
}
}
/* Wait for connections (up to 15s). */
DEBUG_INFO("waiting for connections...");
for (int i = 0; i < 150 && !g_shutdown; i++) {
nostr_relay_pool_run(upstream, 100);
if (i % 50 == 0 && i > 0) {
int connected = 0;
for (int r = 0; r < g_state.relay_count; r++) {
nostr_pool_relay_status_t st =
nostr_relay_pool_get_relay_status(upstream,
g_state.relays[r].url);
g_state.relays[r].is_connected = (st == NOSTR_POOL_RELAY_CONNECTED);
if (g_state.relays[r].is_connected) connected++;
}
DEBUG_INFO(" %d/%d relays connected after %dms",
connected, g_state.relay_count, (i+1)*100);
}
}
/* Log final connection status. */
for (int r = 0; r < g_state.relay_count; r++) {
nostr_pool_relay_status_t st =
nostr_relay_pool_get_relay_status(upstream, g_state.relays[r].url);
g_state.relays[r].is_connected = (st == NOSTR_POOL_RELAY_CONNECTED);
const char *status_str = "?";
if (st == NOSTR_POOL_RELAY_CONNECTED) status_str = "connected";
else if (st == NOSTR_POOL_RELAY_CONNECTING) status_str = "connecting";
else if (st == NOSTR_POOL_RELAY_DISCONNECTED) status_str = "disconnected";
DEBUG_INFO(" %s: %s", g_state.relays[r].url, status_str);
write_raw_log(g_state.relays[r].url, "STATUS: %s", status_str);
}
/* Open subscription: ALL events (no filter). */
cJSON *filter = cJSON_CreateObject();
cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber((double)time(NULL)));
char **listed = NULL;
nostr_pool_relay_status_t *statuses = NULL;
int n = nostr_relay_pool_list_relays(upstream, &listed, &statuses);
const char **urls = NULL;
if (n > 0) {
urls = malloc(n * sizeof(char *));
for (int j = 0; j < n; j++) urls[j] = listed[j];
}
nostr_pool_subscription_t *sub = NULL;
if (n > 0 && urls) {
sub = nostr_relay_pool_subscribe(
upstream, urls, n, filter,
on_event, on_eose, NULL,
0, 1, NOSTR_POOL_EOSE_FULL_SET, 0, 0);
}
free(urls);
free(listed);
free(statuses);
cJSON_Delete(filter);
if (!sub) {
DEBUG_ERROR("failed to open subscription");
} else {
DEBUG_INFO("subscription opened on %d relays", n);
}
/* Main loop. */
g_state.start_time = time(NULL);
g_state.last_summary_log = g_state.start_time;
time_t last_resubscribe = g_state.start_time;
DEBUG_INFO("entering main loop for %ld seconds", duration_seconds);
while (!g_shutdown) {
if (duration_seconds > 0) {
long elapsed = (long)(time(NULL) - g_state.start_time);
if (elapsed >= duration_seconds) {
DEBUG_INFO("duration reached (%lds)", elapsed);
break;
}
}
nostr_relay_pool_run(upstream, 100);
/* Poll connection status. */
for (int r = 0; r < g_state.relay_count; r++) {
nostr_pool_relay_status_t st =
nostr_relay_pool_get_relay_status(upstream, g_state.relays[r].url);
int was = g_state.relays[r].is_connected;
int now = (st == NOSTR_POOL_RELAY_CONNECTED);
g_state.relays[r].is_connected = now;
if (now && !was) {
g_state.relays[r].reconnect_count++;
DEBUG_INFO("[RELAY] %s CONNECTED", g_state.relays[r].url);
write_raw_log(g_state.relays[r].url, "CONNECTED");
} else if (!now && was) {
g_state.relays[r].disconnect_count++;
const char *err = nostr_relay_pool_get_relay_last_connection_error(
upstream, g_state.relays[r].url);
DEBUG_WARN("[RELAY] %s DISCONNECTED: %s",
g_state.relays[r].url, err ? err : "");
write_raw_log(g_state.relays[r].url, "DISCONNECTED: %s", err ? err : "");
}
}
/* Periodic resubscribe (every 5 min) to re-establish REQ on reconnected relays. */
time_t now_t = time(NULL);
if ((now_t - last_resubscribe) >= 300) {
if (sub) {
nostr_pool_subscription_close(sub);
sub = NULL;
}
/* Re-open subscription with fresh filter. */
cJSON *new_filter = cJSON_CreateObject();
cJSON_AddItemToObject(new_filter, "since",
cJSON_CreateNumber((double)time(NULL)));
char **rel_list = NULL;
nostr_pool_relay_status_t *rel_statuses = NULL;
int rel_n = nostr_relay_pool_list_relays(upstream, &rel_list, &rel_statuses);
const char **rel_urls = NULL;
if (rel_n > 0) {
rel_urls = malloc(rel_n * sizeof(char *));
for (int j = 0; j < rel_n; j++) rel_urls[j] = rel_list[j];
}
if (rel_n > 0 && rel_urls) {
sub = nostr_relay_pool_subscribe(
upstream, rel_urls, rel_n, new_filter,
on_event, on_eose, NULL,
0, 1, NOSTR_POOL_EOSE_FULL_SET, 0, 0);
}
free(rel_urls);
free(rel_list);
free(rel_statuses);
cJSON_Delete(new_filter);
if (sub) {
DEBUG_INFO("resubscribed on %d relays", rel_n);
} else {
DEBUG_WARN("resubscribe failed");
}
last_resubscribe = now_t;
}
/* Periodic heartbeat with ping stats. */
if ((now_t - g_state.last_summary_log) >= 30) {
long elapsed = (long)(now_t - g_state.start_time);
double rate = elapsed > 0 ? (double)g_state.total_events / elapsed : 0;
int connected = 0;
for (int r = 0; r < g_state.relay_count; r++)
if (g_state.relays[r].is_connected) connected++;
/* Get ping stats for first connected relay. */
double ping = 0;
int ping_samples = 0;
for (int r = 0; r < g_state.relay_count && ping_samples == 0; r++) {
if (g_state.relays[r].is_connected) {
const nostr_relay_stats_t *stats =
nostr_relay_pool_get_relay_stats(upstream,
g_state.relays[r].url);
if (stats) {
ping = stats->ping_latency_current;
ping_samples = stats->ping_samples;
}
}
}
DEBUG_INFO("[HEARTBEAT] %ld events in %lds (%.1f ev/s) | "
"%d/%d relays | %d kinds | %d pubkeys | "
"ping=%.1fms(%d)",
g_state.total_events, elapsed, rate,
connected, g_state.relay_count,
g_state.kind_count, g_state.pubkey_count,
ping * 1000, ping_samples);
g_state.last_summary_log = now_t;
}
}
if (sub) nostr_pool_subscription_close(sub);
DEBUG_INFO("writing reports...");
write_report();
write_json_stats();
if (g_state.raw_log) {
fprintf(g_state.raw_log, "# Test ended: %s", ctime(&now));
fclose(g_state.raw_log);
DEBUG_INFO("raw log: %s", g_state.raw_log_path);
}
nostr_relay_pool_destroy(upstream);
cr_config_free(&cfg);
nostr_crypto_cleanup();
DEBUG_INFO("clean exit");
return 0;
}
/* ------------------------------------------------------------------ */
/* Standalone entry point */
/* ------------------------------------------------------------------ */
static void usage(const char *prog) {
fprintf(stderr,
"cache_all_test %s - feasibility test for cache-all approach\n"
"\n"
"Usage: %s -c <config.jsonc> -t <seconds> [options]\n"
"\n"
"Options:\n"
" -c, --config <file> Path to .jsonc config file (required)\n"
" -t, --time <seconds> Test duration in seconds (required)\n"
" -d, --debug <level> Log level 0-5 (default: 3)\n"
" -h, --help Show this help\n"
"\n"
"Output files:\n"
" cache_all_test_raw_relay_<ts>.log - verbatim relay responses\n"
" cache_all_test_report_<ts>.txt - summary report\n"
" cache_all_test_stats_<ts>.json - machine-readable stats\n",
CR_VERSION, prog);
}
int main(int argc, char **argv) {
const char *config_path = NULL;
long duration = 0;
int log_level = DEBUG_LEVEL_INFO;
static struct option longopts[] = {
{"config", required_argument, 0, 'c'},
{"time", required_argument, 0, 't'},
{"debug", required_argument, 0, 'd'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
int opt;
while ((opt = getopt_long(argc, argv, "c:t:d:h", longopts, NULL)) != -1) {
switch (opt) {
case 'c': config_path = optarg; break;
case 't': duration = atol(optarg); break;
case 'd': log_level = atoi(optarg); break;
case 'h': usage(argv[0]); return 0;
default: usage(argv[0]); return 1;
}
}
if (!config_path) {
fprintf(stderr, "ERROR: -c <config> is required\n\n");
usage(argv[0]);
return 1;
}
if (duration <= 0) {
fprintf(stderr, "ERROR: -t <seconds> is required and must be > 0\n\n");
usage(argv[0]);
return 1;
}
return run_cache_all_test(config_path, duration, log_level);
}
+26
View File
@@ -0,0 +1,26 @@
/*
* cache_all_test - feasibility test for "cache everything" approach.
*
* Standalone test program that connects to all outbox relays of followed
* pubkeys and subscribes to ALL events (no author filter) to determine
* if relays will tolerate this level of data flow.
*
* Logs raw relay responses verbatim. Tracks kind distribution, event sizes,
* per-relay stats. Generates summary report + raw relay log file.
*
* See plans/cache_all_feasibility_test_plan.md for full design.
*/
#ifndef CACHE_ALL_TEST_H
#define CACHE_ALL_TEST_H
/* Run the cache-all feasibility test.
* config_path: path to .jsonc config file
* duration_seconds: how long to run (0 = run until SIGINT)
* log_level: debug level 0-5
* Returns 0 on success, -1 on error.
*/
int run_cache_all_test(const char *config_path,
long duration_seconds,
int log_level);
#endif /* CACHE_ALL_TEST_H */
+25 -6
View File
@@ -7,6 +7,7 @@
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int cr_follow_is_root(const cr_config_t *cfg, const char *hex) {
if (!cfg->root_hex_ready) return 0;
@@ -16,17 +17,35 @@ int cr_follow_is_root(const cr_config_t *cfg, const char *hex) {
return 0;
}
/* Check if a string is a 64-character hex pubkey (no npub prefix). */
static int is_hex_pubkey(const char *s) {
if (!s || strlen(s) != 64) return 0;
for (int i = 0; i < 64; i++) {
if (!isxdigit((unsigned char)s[i])) return 0;
}
return 1;
}
int cr_follow_decode_roots(cr_config_t *cfg) {
for (int i = 0; i < cfg->root_npub_count; i++) {
unsigned char pubkey[32];
if (nostr_decode_npub(cfg->root_npubs[i], pubkey) != NOSTR_SUCCESS) {
DEBUG_ERROR("follow: failed to decode npub '%s'", cfg->root_npubs[i]);
return -1;
const char *input = cfg->root_npubs[i];
if (is_hex_pubkey(input)) {
/* Already a 64-char hex pubkey — copy directly. */
strncpy(cfg->root_hex[i], input, 64);
cfg->root_hex[i][64] = '\0';
} else {
/* Assume npub (bech32) format — decode to hex. */
unsigned char pubkey[32];
if (nostr_decode_npub(input, pubkey) != NOSTR_SUCCESS) {
DEBUG_ERROR("follow: failed to decode root pubkey '%s' "
"(not a valid npub or 64-char hex)", input);
return -1;
}
nostr_bytes_to_hex(pubkey, 32, cfg->root_hex[i]);
}
nostr_bytes_to_hex(pubkey, 32, cfg->root_hex[i]);
}
cfg->root_hex_ready = 1;
DEBUG_INFO("follow: decoded %d root npubs", cfg->root_npub_count);
DEBUG_INFO("follow: decoded %d root pubkeys", cfg->root_npub_count);
return 0;
}
+29 -8
View File
@@ -593,16 +593,37 @@ int main(int argc, char **argv) {
long events_fetched = live.events_received + bf.events_total;
long inbox_inserts = sink.published_ok;
int connected = 0;
{
char **listed = NULL;
nostr_pool_relay_status_t *statuses = NULL;
int n = nostr_relay_pool_list_relays(upstream, &listed, &statuses);
for (int j = 0; j < n; j++) {
if (statuses[j] == NOSTR_POOL_RELAY_CONNECTED) connected++;
char **listed = NULL;
nostr_pool_relay_status_t *statuses = NULL;
int n = nostr_relay_pool_list_relays(upstream, &listed, &statuses);
if (n > 0) {
/* Build per-relay status arrays for the upstream_relays table. */
const char **urls = malloc((size_t)n * sizeof(char *));
int *codes = malloc((size_t)n * sizeof(int));
const char **texts = malloc((size_t)n * sizeof(char *));
if (urls && codes && texts) {
for (int j = 0; j < n; j++) {
urls[j] = listed[j];
codes[j] = (int)statuses[j];
if (statuses[j] == NOSTR_POOL_RELAY_CONNECTED) {
texts[j] = "connected";
connected++;
} else if (statuses[j] == NOSTR_POOL_RELAY_CONNECTING) {
texts[j] = "connecting";
} else if (statuses[j] == NOSTR_POOL_RELAY_DISCONNECTED) {
texts[j] = "disconnected";
} else {
texts[j] = "error";
}
}
pg_inbox_update_upstream_relays(urls, codes, texts, n);
}
free(listed);
free(statuses);
free(urls);
free(codes);
free(texts);
}
free(listed);
free(statuses);
int bf_complete = 0, bf_total = 0;
pg_inbox_count_backfill_progress(&bf_complete, &bf_total);
pg_inbox_update_status("running", config_generation, (long)now,
+90
View File
@@ -1186,3 +1186,93 @@ int pg_inbox_update_last_event_at(const char *pk, long event_created_at) {
PQclear(res);
return 0;
}
/* ------------------------------------------------------------------ */
/* Upstream relay status tracking */
/* ------------------------------------------------------------------ */
int pg_inbox_update_upstream_relays(const char **relay_urls,
const int *statuses,
const char **status_texts,
int count) {
if (!g_pg) {
DEBUG_ERROR("pg_inbox: not initialized");
return -1;
}
if (count <= 0) return 0;
/* Create the table if it doesn't exist (idempotent). */
PGresult *create = PQexec(g_pg,
"CREATE TABLE IF NOT EXISTS caching_upstream_relays ("
" relay_url TEXT PRIMARY KEY,"
" status_code INT NOT NULL DEFAULT 0,"
" status_text TEXT NOT NULL DEFAULT '',"
" updated_at BIGINT NOT NULL DEFAULT 0"
")");
if (create) {
PQclear(create);
}
/* Build a batch INSERT from the arrays. We use a single multi-row
* INSERT ... ON CONFLICT DO UPDATE so all relays are updated in one
* round-trip. Each row is: (relay_url, status_code, status_text, now). */
/* Calculate total buffer size: each row adds roughly 200 bytes. */
size_t total = count * 200 + 256;
char *sql = malloc(total);
if (!sql) return -1;
char *p = sql;
int written = snprintf(p, total,
"INSERT INTO caching_upstream_relays (relay_url, status_code, status_text, updated_at) VALUES ");
if (written < 0) { free(sql); return -1; }
p += written;
for (int i = 0; i < count; i++) {
/* Escape the relay URL and status text for safe SQL embedding.
* We use PQescapeLiteral for simplicity. */
char *esc_url = PQescapeLiteral(g_pg, relay_urls[i], (int)strlen(relay_urls[i]));
char *esc_text = PQescapeLiteral(g_pg, status_texts[i], (int)strlen(status_texts[i]));
if (!esc_url || !esc_text) {
if (esc_url) PQfreemem(esc_url);
if (esc_text) PQfreemem(esc_text);
free(sql);
return -1;
}
size_t remaining = total - (size_t)(p - sql);
written = snprintf(p, remaining,
"%s(%s, %d, %s, EXTRACT(EPOCH FROM NOW())::BIGINT)",
(i > 0) ? ", " : "",
esc_url, statuses[i], esc_text);
PQfreemem(esc_url);
PQfreemem(esc_text);
if (written < 0 || (size_t)written >= remaining) { free(sql); return -1; }
p += written;
}
/* Add ON CONFLICT clause. */
size_t remaining = total - (size_t)(p - sql);
written = snprintf(p, remaining,
" ON CONFLICT (relay_url) DO UPDATE SET "
" status_code = EXCLUDED.status_code, "
" status_text = EXCLUDED.status_text, "
" updated_at = EXCLUDED.updated_at");
if (written < 0 || (size_t)written >= remaining) { free(sql); return -1; }
PGresult *res = PQexec(g_pg, sql);
free(sql);
if (!res) {
DEBUG_ERROR("pg_inbox: update_upstream_relays returned NULL result");
return -1;
}
ExecStatusType st = PQresultStatus(res);
if (st != PGRES_COMMAND_OK) {
DEBUG_ERROR("pg_inbox: update_upstream_relays failed: %s",
PQresultErrorMessage(res));
PQclear(res);
return -1;
}
PQclear(res);
return 0;
}
+13
View File
@@ -162,4 +162,17 @@ cJSON* pg_inbox_get_authors_for_catchup(void);
* the given value. Returns 0 on success, -1 on error. */
int pg_inbox_update_last_event_at(const char *pk, long event_created_at);
/* ------------------------------------------------------------------ */
/* Upstream relay status tracking */
/* ------------------------------------------------------------------ */
/* Update the upstream relay status table with current connection states.
* Takes parallel arrays of relay_urls, status codes, and status text strings.
* count is the number of entries. Uses a batch upsert.
* Returns 0 on success, -1 on error. */
int pg_inbox_update_upstream_relays(const char **relay_urls,
const int *statuses,
const char **status_texts,
int count);
#endif /* CACHING_RELAY_PG_INBOX_H */
+93 -3
View File
@@ -239,8 +239,8 @@ static void compute_covering_set(cr_relay_map_t *map, cr_config_t *cfg,
* Returns a cJSON array of events (caller must cJSON_Delete each + free).
* Sets *out_count to the number of events returned. */
static cJSON **query_kind10002_batch(nostr_relay_pool_t *pool,
const char **pubkeys, int pubkey_count,
int *out_count) {
const char **pubkeys, int pubkey_count,
int *out_count) {
cJSON *filter = cJSON_CreateObject();
cJSON *authors = cJSON_CreateArray();
for (int i = 0; i < pubkey_count; i++)
@@ -276,6 +276,44 @@ static cJSON **query_kind10002_batch(nostr_relay_pool_t *pool,
return events;
}
/* ---- batched kind-3 fallback query ---- */
/* Query kind 3 (contact list) for a batch of pubkeys from a pool.
* Kind-3 events historically contain "r" tags with relay URLs (pre-NIP-65).
* We use this as a fallback for pubkeys that don't have kind-10002.
* Returns a cJSON array of events (caller must cJSON_Delete each + free).
* Sets *out_count to the number of events returned. */
static cJSON **query_kind3_batch(nostr_relay_pool_t *pool,
const char **pubkeys, int pubkey_count,
int *out_count) {
cJSON *filter = cJSON_CreateObject();
cJSON *authors = cJSON_CreateArray();
for (int i = 0; i < pubkey_count; i++)
cJSON_AddItemToArray(authors, cJSON_CreateString(pubkeys[i]));
cJSON_AddItemToObject(filter, "authors", authors);
cJSON *kinds = cJSON_CreateArray();
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(3));
cJSON_AddItemToObject(filter, "kinds", kinds);
/* limit=1 per author: we only need the most recent kind-3.
* The relay pool query_sync returns the most recent events. */
cJSON_AddItemToObject(filter, "limit", cJSON_CreateNumber(pubkey_count));
const char **urls = NULL;
int n = get_pool_urls(pool, &urls);
DEBUG_TRACE("query_kind3_batch: querying %d relays for %d pubkeys", n, pubkey_count);
int ev_count = 0;
cJSON **events = nostr_relay_pool_query_sync(pool, urls, n, filter, &ev_count, 30000);
free(urls);
cJSON_Delete(filter);
DEBUG_TRACE("query_kind3_batch: returned %d events", ev_count);
*out_count = ev_count;
return events;
}
/* Process a batch of kind-10002 events: parse r-tags, populate outbox entries,
* publish to local relay. Returns count of events processed. */
static int process_10002_batch(cJSON **events, int ev_count,
@@ -438,9 +476,61 @@ int cr_relay_discovery_run(cr_relay_map_t *map, cr_config_t *cfg,
free(remaining_pk);
}
/* Phase 3: Kind-3 fallback for pubkeys still without relay lists.
* Kind-3 (contact list) events historically contain "r" tags with relay
* URLs (pre-NIP-65). This catches older users who never published kind-10002. */
int remaining_k3 = 0;
for (int i = 0; i < followed->count; i++) if (!found[i]) remaining_k3++;
if (remaining_k3 > 0) {
DEBUG_INFO("relay_discovery: querying bootstrap relays for kind-3 fallback (%d pubkeys)...",
remaining_k3);
const char **remaining_pk = malloc(remaining_k3 * sizeof(char *));
int ridx = 0;
for (int i = 0; i < followed->count; i++) {
if (!found[i]) remaining_pk[ridx++] = followed->items[i];
}
int found_k3 = 0;
for (int start = 0; start < remaining_k3 && !g_shutdown; start += CR_10002_BATCH_SIZE) {
int batch = remaining_k3 - start;
if (batch > CR_10002_BATCH_SIZE) batch = CR_10002_BATCH_SIZE;
int ev_count = 0;
cJSON **events = query_kind3_batch(upstream,
&remaining_pk[start],
batch, &ev_count);
if (events && ev_count > 0) {
for (int k = 0; k < ev_count; k++) {
cJSON *pk = cJSON_GetObjectItem(events[k], "pubkey");
if (pk && cJSON_IsString(pk)) {
const char *hex = cJSON_GetStringValue(pk);
for (int i = 0; i < followed->count; i++) {
if (strcmp(followed->items[i], hex) == 0) {
if (!found[i]) { found[i] = 1; found_k3++; }
break;
}
}
}
}
/* process_10002_batch works for kind-3 too — same r-tag format */
process_10002_batch(events, ev_count, map, sink, "bootstrap");
cr_sink_pump(sink, 500);
} else {
free(events);
}
}
free(remaining_pk);
DEBUG_INFO("relay_discovery: kind-3 fallback found %d additional pubkeys with relays", found_k3);
}
found_none = followed->count - found_local - found_bootstrap;
/* Recalculate found_none after kind-3 fallback */
int still_none = 0;
for (int i = 0; i < followed->count; i++) if (!found[i]) still_none++;
DEBUG_INFO("relay_discovery: %d local, %d bootstrap, %d none",
found_local, found_bootstrap, found_none);
found_local, found_bootstrap, still_none);
free(found);
+11 -2
View File
@@ -181,8 +181,16 @@ echo "[remote] Health checks"
sudo systemctl --no-pager --full status "$SERVICE_NAME" | sed -n '1,25p'
sudo -u "$RELAY_USER" psql -d crelay -c "SELECT current_user, current_database();"
# Verify public relay page is accessible (HTTP 200, no auth).
PUBLIC_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1/relay/" 2>/dev/null || echo "000")
if [ "$PUBLIC_CODE" = "200" ]; then
echo " -> public relay page accessible (HTTP $PUBLIC_CODE)"
else
echo " -> WARNING: public relay page returned HTTP $PUBLIC_CODE (expected 200)"
fi
# Verify admin page is accessible (HTTP 200 or 401 = auth required, both are OK).
ADMIN_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1/admin/" 2>/dev/null || echo "000")
ADMIN_CODE=$(curl -s -o /dev/null -w "%{http_code}" "http://127.0.0.1/relay/admin/" 2>/dev/null || echo "000")
if [ "$ADMIN_CODE" = "200" ] || [ "$ADMIN_CODE" = "401" ]; then
echo " -> admin page accessible (HTTP $ADMIN_CODE)"
else
@@ -199,5 +207,6 @@ rm -f /tmp/admin_deploy.tar.gz
echo "Deployment complete: $REMOTE_HOST"
echo ""
echo "Admin interface: https://laantungir.net/admin/"
echo "Public relay page: https://laantungir.net/relay/"
echo "Admin interface: https://laantungir.net/relay/admin/ (Basic Auth)"
echo "Caching is NOT auto-started. Enable it via the admin UI when ready."
+57
View File
@@ -361,6 +361,63 @@ server {
}
```
#### PHP Admin UI: Public Stats vs Authenticated Admin
The relay ships with a PHP admin UI (in [`admin/`](../admin/)) that talks
directly to PostgreSQL. It is served by nginx + php-fpm and is split into two
URL paths with different access controls:
| URL | Auth | Purpose |
|----------------------------------|---------------|----------------------------------------------------|
| `https://<domain>/relay/` | None (public) | Public landing page: relay info + event-rate chart |
| `https://<domain>/relay/api/chart.php?range=day` | None (public) | Read-only ASCII chart endpoint (aggregate COUNT) |
| `https://<domain>/relay/admin/` | HTTP Basic Auth | Full admin UI: config, auth rules, IP bans, DMs, SQL query |
**Why Basic Auth is required for `/relay/admin/`:** the PHP endpoints have no
server-side authentication of their own. The `nostr_login_lite` modal in the
admin UI is a **client-side only** gate (it only toggles UI visibility in the
browser); it does not send a signed event or session token to the backend.
Without Basic Auth, anyone could hit `api/query.php` to run SELECT queries
against your database, `api/config.php` to change relay config, `api/auth.php`
to edit blacklist/whitelist rules, or `api/dm.php` to read DMs.
Only [`admin/api/chart.php`](../admin/api/chart.php) is safe to expose
publicly — it runs a fixed aggregate `COUNT(*)` query with the only user input
being the `range` selector, which is validated against a whitelist
(`hour`/`day`/`month`/`year`).
A complete, ready-to-use nginx config including this split is in
[`examples/deployment/nginx-proxy/nginx.conf`](../examples/deployment/nginx-proxy/nginx.conf).
The relevant blocks:
```nginx
# Public landing page + chart (no auth)
location /relay/ {
alias /opt/c-relay-pg/admin/public/;
index index.php;
location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; ... }
}
location ^~ /relay/api/chart.php { alias /opt/c-relay-pg/admin/api/chart.php; ... }
location ^~ /relay/assets/ { alias /opt/c-relay-pg/admin/assets/; }
# Authenticated admin UI (Basic Auth)
location /relay/admin/ {
alias /opt/c-relay-pg/admin/;
auth_basic "Relay Admin";
auth_basic_user_file /opt/c-relay-pg/admin/.htpasswd;
location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; ... }
location ^~ /relay/admin/lib/ { deny all; } # DB credentials
location ^~ /relay/admin/public/ { deny all; } # served via /relay/
}
```
Create the Basic Auth credentials on the server:
```bash
sudo htpasswd -c /opt/c-relay-pg/admin/.htpasswd <username>
sudo systemctl reload nginx
```
### Apache Configuration
#### WebSocket Proxy with mod_proxy_wstunnel
+2 -2
View File
@@ -185,7 +185,7 @@ perform_backup() {
print_step "Database size: $db_size"
# Create SQLite backup using .backup command (hot backup)
if sqlite3 "$DB_FILE" ".backup $backup_file" 2>/dev/null; then
if pg_dump -d crelay > "$backup_file"
print_success "Database backup created: $backup_file"
else
# Fallback to file copy if .backup fails
@@ -203,7 +203,7 @@ perform_backup() {
# Check backup integrity
if [[ "$VERIFY" == "true" ]]; then
print_step "Verifying backup integrity..."
if sqlite3 "$backup_file" "PRAGMA integrity_check;" | grep -q "ok"; then
if pg_restore -l "$backup_file" &>/dev/null
print_success "Backup integrity verified"
else
print_error "Backup integrity check failed"
@@ -247,7 +247,7 @@ check_database_integrity() {
local integrity_ok=true
for db_file in "${db_files[@]}"; do
if [[ -r "$db_file" ]]; then
if timeout 30 sqlite3 "$db_file" "PRAGMA integrity_check;" | grep -q "ok"; then
if timeout 30 psql -d crelay "PRAGMA integrity_check;" | grep -q "ok"; then
print_success "Database integrity OK: $(basename "$db_file")"
else
print_error "Database integrity failed: $(basename "$db_file")"
@@ -292,7 +292,7 @@ check_configuration_events() {
local config_count=0
for db_file in "${db_files[@]}"; do
if [[ -r "$db_file" ]]; then
local count=$(sqlite3 "$db_file" "SELECT COUNT(*) FROM events WHERE kind = 33334;" 2>/dev/null || echo "0")
local count=$(psql -d crelay "SELECT COUNT(*) FROM events WHERE kind = 33334;" 2>/dev/null || echo "0")
config_count=$((config_count + count))
fi
done
+64 -4
View File
@@ -155,10 +155,64 @@ http {
log_not_found off;
}
# PHP admin page for caching service (direct PostgreSQL access).
# Bypasses the NIP-44 64KB encryption limit of the Nostr admin API.
# ---------------------------------------------------------------
# Public relay landing page + public chart endpoint.
#
# https://<domain>/relay/ -> public index.php
# https://<domain>/relay/api/chart.php?range=... -> public ASCII chart
# https://<domain>/relay/assets/... -> public CSS/JS
#
# No authentication. Exposes ONLY read-only, non-sensitive data:
# relay name/description/pubkey and the aggregate event-rate chart.
# All sensitive admin endpoints live under /relay/admin/ (see below).
# Requires: php-fpm + php-pgsql installed and configured.
location /admin/ {
# ---------------------------------------------------------------
location /relay/ {
alias /opt/c-relay-pg/admin/public/;
index index.php;
# Pass .php files to PHP-FPM
location ~ \.php$ {
# Adjust socket path for your distro:
# Debian/Ubuntu: unix:/run/php/php8.2-fpm.sock
# RHEL/Fedora: unix:/run/php-fpm/www.sock
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
}
# Public chart endpoint — the public index.php fetches this relatively
# as "api/chart.php". Map it to the admin's chart.php (read-only,
# aggregate COUNT query; safe to expose). Only chart.php is served
# here; all other admin/api/*.php endpoints stay behind /relay/admin/.
location ^~ /relay/api/chart.php {
alias /opt/c-relay-pg/admin/api/chart.php;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_index chart.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
# Public static assets (CSS/JS) for the public landing page.
# The public index.php references these relatively as "assets/...".
location ^~ /relay/assets/ {
alias /opt/c-relay-pg/admin/assets/;
}
# ---------------------------------------------------------------
# Authenticated admin UI (full PHP admin).
#
# https://<domain>/relay/admin/ -> admin index.php
# https://<domain>/relay/admin/api/*.php -> admin API endpoints
#
# Protected by HTTP Basic Auth. The PHP endpoints have NO server-side
# auth of their own (the nostr_login_lite modal is client-side only),
# so this Basic Auth gate is what actually secures config edits, auth
# rules, IP bans, DMs, and raw SQL queries.
# ---------------------------------------------------------------
location /relay/admin/ {
alias /opt/c-relay-pg/admin/;
index index.php;
@@ -178,7 +232,13 @@ http {
}
# Deny access to the lib/ directory (contains DB credentials)
location ^~ /admin/lib/ {
location ^~ /relay/admin/lib/ {
deny all;
}
# Deny access to the public/ directory from the admin path
# (it is served publicly via /relay/ instead).
location ^~ /relay/admin/public/ {
deny all;
}
}
+2 -2
View File
@@ -60,12 +60,12 @@ install_dependencies() {
if [[ $OS == "debian" ]]; then
apt update
apt install -y build-essential git sqlite3 libsqlite3-dev \
apt install -y build-essential git postgresql postgresql-client
libwebsockets-dev libssl-dev libsecp256k1-dev \
libcurl4-openssl-dev zlib1g-dev systemd curl wget
elif [[ $OS == "redhat" ]]; then
yum groupinstall -y "Development Tools"
yum install -y git sqlite-devel libwebsockets-devel \
yum install -y git postgresql-devel postgresql-server-devel
openssl-devel libsecp256k1-devel libcurl-devel \
zlib-devel systemd curl wget
fi
+33 -50
View File
@@ -8,14 +8,13 @@ echo "=== C Nostr Relay Build and Restart Script ==="
# Parse command line arguments
PRESERVE_DATABASE=false
HELP=false
USE_TEST_KEYS=false
USE_TEST_KEYS=true
ADMIN_KEY=""
RELAY_KEY=""
PORT_OVERRIDE=""
DEBUG_LEVEL="5"
START_CACHING=false
RESET_BACKFILL=false
DB_BACKEND="postgres"
DB_CONNSTRING=""
DB_HOST=""
DB_PORT=""
@@ -87,24 +86,10 @@ while [[ $# -gt 0 ]]; do
;;
--test-keys|-t)
USE_TEST_KEYS=true
# Read keys from .test_keys file
if [ -f ".test_keys" ]; then
echo "Reading test keys from .test_keys file..."
# Source the file to get the variables
source .test_keys
# Remove any single quotes from the values
# Note: -a flag expects ADMIN_PUBKEY (public key), not ADMIN_PRIVKEY
ADMIN_KEY=$(echo "$ADMIN_PUBKEY" | tr -d "'")
RELAY_KEY=$(echo "$SERVER_PRIVKEY" | tr -d "'")
echo "Using admin pubkey from .test_keys: ${ADMIN_KEY:0:16}..."
echo "Using relay privkey from .test_keys: ${RELAY_KEY:0:16}..."
else
echo "ERROR: .test_keys file not found"
echo "Please create a .test_keys file with the following format:"
echo " ADMIN_PUBKEY='your_admin_public_key_hex'"
echo " SERVER_PRIVKEY='your_relay_private_key_hex'"
exit 1
fi
shift
;;
--no-test-keys|--production)
USE_TEST_KEYS=false
shift
;;
--debug-level=*)
@@ -135,20 +120,6 @@ while [[ $# -gt 0 ]]; do
shift 2
fi
;;
--db-backend)
if [ -z "$2" ]; then
echo "ERROR: --db-backend requires a value (sqlite|postgres)"
HELP=true
shift
else
DB_BACKEND="$2"
shift 2
fi
;;
--db-backend=*)
DB_BACKEND="${1#*=}"
shift
;;
--db-connstring)
if [ -z "$2" ]; then
echo "ERROR: --db-connstring requires a value"
@@ -238,6 +209,24 @@ if [ -n "$RELAY_KEY" ]; then
fi
fi
# Load test keys from .test_keys file if USE_TEST_KEYS is true and no explicit keys given
if [ "$USE_TEST_KEYS" = true ] && [ -z "$ADMIN_KEY" ] && [ -z "$RELAY_KEY" ]; then
if [ -f ".test_keys" ]; then
echo "Reading test keys from .test_keys file..."
source .test_keys
ADMIN_KEY=$(echo "$ADMIN_PUBKEY" | tr -d "'")
RELAY_KEY=$(echo "$SERVER_PRIVKEY" | tr -d "'")
echo "Using admin pubkey from .test_keys: ${ADMIN_KEY:0:16}..."
echo "Using relay privkey from .test_keys: ${RELAY_KEY:0:16}..."
else
echo "ERROR: .test_keys file not found"
echo "Please create a .test_keys file with the following format:"
echo " ADMIN_PUBKEY='your_admin_public_key_hex'"
echo " SERVER_PRIVKEY='your_relay_private_key_hex'"
exit 1
fi
fi
# Validate port if provided
if [ -n "$PORT_OVERRIDE" ]; then
if ! [[ "$PORT_OVERRIDE" =~ ^[0-9]+$ ]] || [ "$PORT_OVERRIDE" -lt 1 ] || [ "$PORT_OVERRIDE" -gt 65535 ]; then
@@ -259,12 +248,6 @@ if [ -n "$DEBUG_LEVEL" ]; then
fi
fi
# Validate DB backend
if [ "$DB_BACKEND" != "sqlite" ] && [ "$DB_BACKEND" != "postgres" ]; then
echo "ERROR: Invalid --db-backend value '$DB_BACKEND'. Use sqlite or postgres."
exit 1
fi
# Validate DB port if provided
if [ -n "$DB_PORT" ]; then
if ! [[ "$DB_PORT" =~ ^[0-9]+$ ]] || [ "$DB_PORT" -lt 1 ] || [ "$DB_PORT" -gt 65535 ]; then
@@ -273,7 +256,7 @@ if [ -n "$DB_PORT" ]; then
fi
fi
if [ "$DB_BACKEND" = "postgres" ] && [ -z "$DB_CONNSTRING" ]; then
if [ -z "$DB_CONNSTRING" ]; then
[ -z "$DB_HOST" ] && DB_HOST="localhost"
[ -z "$DB_PORT" ] && DB_PORT="5432"
[ -z "$DB_NAME" ] && DB_NAME="crelay"
@@ -361,8 +344,8 @@ if [ "$HELP" = true ]; then
echo " -p, --port <port> Custom port override (default: 8888)"
echo " -d, --debug-level <0-5> Set debug level: 0=none, 1=errors, 2=warnings, 3=info, 4=debug, 5=trace"
echo " --preserve-database Keep existing database files (don't delete for fresh start)"
echo " --test-keys, -t Use deterministic test keys for development (admin: all 'a's, relay: all '1's)"
echo " --db-backend <name> Database backend: postgres (default) or sqlite"
echo " --test-keys, -t Use deterministic test keys for development (admin: all 'a's, relay: all '1's) [default]"
echo " --no-test-keys, --production Generate random keys (production mode)"
echo " --db-connstring <str> PostgreSQL libpq connection string"
echo " --db-host <host> PostgreSQL host"
echo " --db-port <port> PostgreSQL port"
@@ -379,7 +362,7 @@ if [ "$HELP" = true ]; then
echo " Database file: <relay_pubkey>.db (created automatically)"
echo ""
echo "Examples:"
echo " $0 # Fresh start with random keys"
echo " $0 # Fresh start with test keys (default)"
echo " $0 -a <admin-hex> -r <relay-hex> # Use custom keys"
echo " $0 -a <admin-hex> -p 9000 # Custom admin key on port 9000"
echo " $0 -p 7777 --strict-port # Fail if port 7777 unavailable (no fallback)"
@@ -387,8 +370,8 @@ if [ "$HELP" = true ]; then
echo " $0 --debug-level=3 # Start with debug level 3 (info)"
echo " $0 -d=5 # Start with debug level 5 (trace)"
echo " $0 --preserve-database # Preserve existing database and keys"
echo " $0 --test-keys # Use test keys for consistent development"
echo " $0 -t --preserve-database # Use test keys and preserve database"
echo " $0 --no-test-keys # Generate random keys (production)"
echo " $0 --no-test-keys --preserve-database # Production mode with database preservation"
echo ""
echo "Default PostgreSQL connection (when no DB flags provided):"
echo " host=localhost port=5432 dbname=crelay user=crelay password=crelay"
@@ -416,7 +399,7 @@ fi
rm -rf dev-config/ 2>/dev/null
rm -f db/c_nostr_relay.db* 2>/dev/null
if [ "$DB_BACKEND" = "postgres" ] && [ -z "$DB_CONNSTRING" ]; then
if [ -z "$DB_CONNSTRING" ]; then
ensure_postgres_database || exit 1
fi
@@ -424,9 +407,9 @@ fi
echo "Embedding web files..."
./embed_web_files.sh
# Build the project - ONLY static build
echo "Building project (static binary, backend: $DB_BACKEND)..."
./build_static.sh --db-backend "$DB_BACKEND"
# Build the project - ONLY static build (PostgreSQL backend)
echo "Building project (static binary, backend: postgres)..."
./build_static.sh
# Exit if static build fails - no fallback
if [ $? -ne 0 ]; then
+441
View File
@@ -0,0 +1,441 @@
# Cache-All Feasibility Test Program
## Goal
Build a standalone C test program that connects to all outbox relays of followed
pubkeys and attempts to subscribe to **all events** (no author filter) to
determine if relays will tolerate this level of data flow. The program logs all
relay interactions (disconnections, rate limiting, CLOSED messages, NOTICE
messages) so we can assess feasibility before modifying the caching daemon.
## Background
The current caching daemon subscribes only to events from followed pubkeys
using `authors=[followed_set]` filters. The proposed "cache everything" approach
would subscribe to all events on those relays and prune old non-followed events
later. The key unknown is whether relays will allow this — they may disconnect
or rate-limit clients that request too much data.
## Preliminary Probe Results (60s on 3 major relays)
Before designing the test program, we ran 60-second probes using `nak req --stream`
with no filter (all events) against 3 major relays to measure real-world data volume
and kind distribution.
### Relay Connectivity
| Relay | Events/60s | Avg Bytes | Max Event | Unique Pubkeys | Notes |
|-------|-----------|-----------|-----------|----------------|-------|
| nos.lol | 2,634 | 1,713 | 44,372 (kind 30089) | 1,228 | Connected OK |
| relay.primal.net | 1,547 | 1,920 | 207,377 (kind 3) | 613 | Connected OK |
| relay.damus.io | 0 | — | — | — | HTTP 503 (unavailable) |
### Kind Distribution (nos.lol + primal combined, ~4,181 events)
| Kind | Description | Count | % | Avg Bytes | Notes |
|------|-------------|-------|---|-----------|-------|
| **21059** | Gift wrap (NIP-59) | 1,063 | 25.4% | 2,574 | Encrypted DMs — large, noisy |
| **5** | Deletion requests | 592 | 14.2% | 447 | Very common |
| **30078** | App-specific data | 173 | 4.1% | 3,670 | Variable size, can be large |
| **20001** | Ephemeral (key exchange?) | 253 | 6.0% | 849 | Short-lived |
| **22580** | WebRTC signaling | 193 | 4.6% | 795 | Noise |
| **1059** | Gift wrap seal (NIP-59) | 154 | 3.7% | 3,023 | Encrypted wrapper |
| **22734** | WebRTC signaling | 168 | 4.0% | 427 | Noise |
| **1** | Text notes | 58 | 1.4% | 812 | **Primary content** |
| **7** | Reactions | 59 | 1.4% | 534 | Likes |
| **0** | Profile metadata | 33 | 0.8% | 985 | Replaceable |
| **3** | Follow lists | 10 | 0.2% | 43,077 | Replaceable, can be huge |
| **10002** | Relay lists | 55 | 1.3% | 418 | Small |
| **9735** | Zap receipts | 22 | 0.5% | 2,110 | Payment confirmations |
| **6** | Reposts | 11 | 0.3% | 1,876 | |
| **30023** | Long-form articles | 3 | 0.1% | 6,119 | |
| **30089** | Chunked data | 16 | 0.4% | 43,907 | **Very large** (avg 44KB!) |
| **30001** | Lists (encrypted) | 4 | 0.1% | 30,538 | Large encrypted content |
| **30815** | Ephemeral large | 4 | 0.1% | 37,534 | **Very large** (avg 37KB!) |
| **25555** | App data | 31 | 0.7% | 5,115 | |
| **13194** | NWC info | 15 | 0.4% | 514 | Wallet connect |
| **445** | Encrypted group msg | 22 | 0.5% | 1,452 | |
| **Other** | 90+ other kinds | ~600 | ~14% | varies | Long tail of app-specific kinds |
### Key Findings
1. **Gift wrap (kind 21059) dominates**: 25% of all events are NIP-59 gift wraps
(encrypted DMs). These are large (~2.5KB avg) and mostly noise for a caching relay.
They have `expiration` tags and are ephemeral by nature.
2. **Deletion requests (kind 5) are extremely common**: 14% of all events. This is
surprising — relays are very busy processing deletes.
3. **WebRTC signaling (kinds 22580, 22734, etc.) is ~10% of traffic**: These are
ephemeral signaling events for video/voice calls. Pure noise for caching.
4. **"Social" content (kinds 1, 7, 6, 0, 3) is only ~4% of total events**: The
vast majority of relay traffic is NOT the content users actually see.
5. **Some kinds are extremely large**: Kind 30089 averages 44KB, kind 30001 averages
30KB, kind 30815 averages 37KB. These would consume significant storage.
6. **Data volume is manageable**: ~2,600-4,200 events/min across 2 relays. At this
rate, a 24h test would collect ~3.7-6M events. Storage would be ~6-10 GB of raw
JSON.
7. **Damus.io is unavailable**: Returns HTTP 503. This may be temporary or may
indicate they block unfiltered subscriptions.
### Implications for the Test Program
- **Subscribe to ALL kinds** for the test — we need to measure which kinds relays
actually send and whether they tolerate the volume.
- **Do NOT save full event JSON** during the test — at ~2MB/min, a 24h run would
produce ~3GB of raw data per relay. Instead, log kind/pubkey/size summaries.
- **Track kind 21059 (gift wrap) separately** — it's the dominant kind and may
need special handling in the real implementation.
- **Monitor for CLOSED messages** — if relays start closing subscriptions due to
volume, we'll see it in the raw relay log.
## Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ cache_all_feasibility_test │
│ │
│ 1. Load root npubs from config.jsonc │
│ 2. Resolve follow graph (kind-3) → followed set │
│ 3. Discover outbox relays (kind-10002) for each follow │
│ 4. Compute minimum covering set of relays │
│ 5. Connect to all covering relays │
│ 6. Subscribe to ALL events (no authors filter) on each │
│ 7. Log every event received (counts + stats, not full JSON)│
│ 8. Log every relay message: CLOSED, NOTICE, EOSE, error │
│ 9. Run for configurable duration (default 24h) │
│ 10. Save summary report + per-relay stats to files │
└─────────────────────────────────────────────────────────────┘
```
## File Structure
All new files go in `caching/` directory, alongside the existing daemon:
```
caching/
├── Makefile # Modified: add test target
├── src/
│ ├── main.c # Existing daemon (unchanged)
│ ├── cache_all_test.c # NEW: test program entry point
│ ├── cache_all_test.h # NEW: test program header
│ ├── debug.c / debug.h # Existing (reused)
│ ├── config.c / config.h # Existing (reused for config loading)
│ ├── state.c / state.h # Existing (reused for pubkey set)
│ ├── follow_graph.c / follow_graph.h # Existing (reused)
│ ├── relay_discovery.c / relay_discovery.h # Existing (reused)
│ └── ... # Other existing files unchanged
```
## Detailed Design
### 1. Config Loading (reuse existing)
Reuse [`caching/src/config.c`](caching/src/config.c) and
[`caching/src/config.h`](caching/src/config.h) to load root npubs, upstream
relays, kinds, and follow graph settings from the same `.jsonc` config file
the daemon uses.
### 2. Follow Graph Resolution (reuse existing)
Reuse [`caching/src/follow_graph.c`](caching/src/follow_graph.c) to:
- Decode root npubs to hex
- Query each root's most recent kind-3 contact list from bootstrap relays
- Build the `cr_pubkey_set_t` of followed pubkeys
### 3. Relay Discovery (reuse existing)
Reuse [`caching/src/relay_discovery.c`](caching/src/relay_discovery.c) to:
- Query kind-10002 for each followed pubkey
- Parse "r" tags to build outbox relay map
- Compute minimum covering set of relays
### 4. Subscription Strategy
For each relay in the covering set, open a subscription with:
```c
// Filter: ALL events (no authors, no kinds, no limit)
cJSON *filter = cJSON_CreateObject();
cJSON_AddItemToObject(filter, "since", cJSON_CreateNumber((double)time(NULL)));
```
This requests every new event from `now` onward on that relay.
**Key difference from existing daemon:** No `authors` filter. This means we
receive events from *everyone* on that relay, not just followed pubkeys.
### 5. Event Handling & Logging
The `on_event` callback should:
1. **Count events per relay** (maintain a per-relay counter)
2. **Count events per kind** (maintain a kind distribution map)
3. **Count events per pubkey** (track which pubkeys are most active)
4. **Log at INFO level** every N events (e.g., every 1000) with summary stats
5. **Do NOT save full event JSON to disk during the run** (would be too much data)
6. **Periodically checkpoint** (every 5 min) a summary to a file
### 6. Relay Interaction Logging — Raw Relay Responses
**Critical requirement:** Log the **actual raw response text** from the relay,
not an interpreted summary. The relay's own words are what matter.
The `on_event`, `on_eose`, and subscription status callbacks from
[`nostr_core_lib`](../nostr_core_lib) provide status strings and message
content. These must be logged **verbatim**.
#### What to log and how:
| Trigger | Log Level | What to Log (verbatim relay text) |
|---------|-----------|-----------------------------------|
| EOSE | INFO | `[RELAY] <url> EOSE` |
| CLOSED | WARN | `[RELAY] <url> CLOSED: <relay's exact reason text>` |
| NOTICE | WARN | `[RELAY] <url> NOTICE: <relay's exact notice text>` |
| Error | ERROR | `[RELAY] <url> ERROR: <relay's exact error message>` |
| Disconnect | WARN | `[RELAY] <url> DISCONNECTED: <transport-level error if any>` |
| Reconnect | INFO | `[RELAY] <url> RECONNECTED` |
| OK (publish) | INFO | `[RELAY] <url> OK: <event_id> <relay's exact message>` |
**Do NOT** categorize or summarize. If a relay says:
```
"rate-limited: please wait 60 seconds before sending new requests"
```
log that exact string. Do NOT log just `"rate-limited"`.
#### Raw Relay Log File
In addition to the normal debug log output, write a **raw relay log file**:
```
cache_all_test_raw_relay_<timestamp>.log
```
This file contains **only** relay responses, one per line, in this format:
```
[TIMESTAMP] [RELAY] <relay_url> <RAW_RESPONSE>
```
Example:
```
[2026-08-02 13:01:00] [RELAY] wss://relay.damus.io CLOSED: "rate-limited: please wait 60 seconds before sending new requests"
[2026-08-02 13:01:05] [RELAY] wss://relay.damus.io RECONNECTED
[2026-08-02 13:01:10] [RELAY] wss://relay.damus.io NOTICE: "too many subscriptions, closing oldest"
[2026-08-02 13:02:00] [RELAY] wss://relay.primal.net EOSE
[2026-08-02 13:02:01] [RELAY] wss://relay.primal.net ERROR: "connection closed unexpectedly"
```
This file is append-only and can be `tail -f`'d during the test run.
#### Event Logging
For events received, log at TRACE level (not to the raw relay log):
```
[TRACE] [EVENT] relay=<url> kind=<N> pubkey=<first8chars>... id=<first8chars>...
```
This gives enough to correlate without flooding the log with full JSON.
Every 1000 events, log a summary at INFO level:
```
[INFO] [STATS] 5000 events received total | relayA: 3200 relayB: 1800 | kinds: 1=4500 7=500
```
### 7. Summary Report
At the end of the run (or on SIGINT/SIGTERM), write a report file:
```
cache_all_test_report_<timestamp>.txt
```
Contents:
```
=== Cache-All Feasibility Test Report ===
Duration: 24h 3m 12s
Config: ./caching_relay_config.jsonc
=== Relay Summary ===
Relay Events EOSE CLOSED NOTICE Errors Status
wss://relay.example.com 124532 12 0 2 0 OK
wss://relay2.example.com 0 0 3 5 2 BLOCKED
=== Kind Distribution ===
Kind Count %
0 1,234 0.5%
1 234,567 94.2%
3 567 0.2%
7 12,345 5.0%
9734 234 0.1%
=== Top 10 Pubkeys by Event Count ===
pubkey_hex_here... 12,345 events
pubkey_hex_here... 8,901 events
...
=== Raw Relay Response Log ===
[2026-08-02 13:01:00] wss://relay.damus.io CLOSED: "rate-limited: please wait 60 seconds before sending new requests"
[2026-08-02 13:01:05] wss://relay.damus.io RECONNECTED
[2026-08-02 13:01:10] wss://relay.damus.io NOTICE: "too many subscriptions, closing oldest"
[2026-08-02 13:02:00] wss://relay.primal.net EOSE
[2026-08-02 13:02:01] wss://relay.primal.net ERROR: "connection closed unexpectedly"
...
```
The Raw Relay Response Log section is a copy of the raw relay log file
([`cache_all_test_raw_relay_<timestamp>.log`](caching/cache_all_test_raw_relay_20260802_130000.log)).
It contains the **verbatim** text from each relay response, not interpreted
or summarized.
### 8. Per-Relay Stats File
Additionally, write a JSON file with per-relay detailed stats:
```
cache_all_test_stats_<timestamp>.json
```
This can be used for programmatic analysis. It includes the raw relay
response text for each interaction, not just counts.
## Implementation Steps
### Step 1: Create `caching/src/cache_all_test.h`
Header file declaring the test program's public interface:
```c
#ifndef CACHE_ALL_TEST_H
#define CACHE_ALL_TEST_H
/* Run the cache-all feasibility test.
* config_path: path to .jsonc config file
* duration_seconds: how long to run (0 = run until SIGINT)
* log_level: debug level 0-5
* Returns 0 on success, -1 on error.
*/
int run_cache_all_test(const char *config_path,
long duration_seconds,
int log_level);
#endif
```
### Step 2: Create `caching/src/cache_all_test.c`
Main implementation file with these sections:
1. **Includes and forward declarations**
2. **Per-relay stats tracking structure**
3. **Global stats accumulator**
4. **Callback implementations** (on_event, on_eose, on_status)
5. **Report generation** (write summary + JSON stats)
6. **Main entry point** (`run_cache_all_test`)
### Step 3: Modify `caching/Makefile`
Add a new target `cache_all_test` that compiles the test program:
```makefile
TEST_SRC = src/cache_all_test.c src/debug.c src/jsonc_strip.c src/config.c \
src/state.c src/follow_graph.c src/relay_discovery.c
cache_all_test: $(TEST_SRC) $(NOSTR_CORE_LIB)
# ... compile to ../build/cache_all_test
```
### Step 4: Build and Run
```bash
cd caching && make cache_all_test
./build/cache_all_test -c caching_relay_config.jsonc -d 3 -t 86400
```
## Output Files
| File | Contents |
|------|----------|
| `cache_all_test_raw_relay_<timestamp>.log` | **Primary output.** One line per relay response, verbatim text. Can be `tail -f`'d live. |
| `cache_all_test_report_<timestamp>.txt` | Summary report with counts, kind distribution, top pubkeys, and raw relay log section. |
| `cache_all_test_stats_<timestamp>.json` | Machine-readable JSON with per-relay stats including raw response texts. |
## What We're Measuring
1. **Relay tolerance**: Do relays CLOSE our subscription or disconnect us?
2. **Rate limiting**: How often do we get rate-limited? What are the cooldown periods?
3. **Data volume**: How many events per hour per relay? What's the kind distribution?
4. **Connection stability**: How often do relays drop us? Do they allow reconnection?
5. **Pubkey diversity**: How many unique pubkeys are posting? What's the ratio of followed vs non-followed events?
## Success Criteria
The test is considered a **success** (feasible) if:
- At least 80% of relays maintain the subscription for the full duration
- Rate limiting events are infrequent (< 5 per relay per day)
- No relay permanently bans or blacklists the connection
- Data volume is manageable (under ~1M events/day total)
The test is considered a **failure** (not feasible) if:
- Most relays CLOSE the subscription within minutes
- Rate limiting is constant (every few minutes)
- Multiple relays permanently disconnect
## Non-Goals
- This test does NOT save events to PostgreSQL
- This test does NOT modify the existing caching daemon
- This test does NOT implement pruning logic
- This test does NOT need to be efficient for production use
## Mermaid Diagram
```mermaid
flowchart TD
A[Start] --> B[Load config from .jsonc]
B --> C[Resolve follow graph kind-3]
C --> D[Discover outbox relays kind-10002]
D --> E[Compute min covering set]
E --> F[Connect to all covering relays]
F --> G[Subscribe to ALL events no authors filter]
G --> H{Test duration reached or SIGINT?}
H -->|No| I[Pump relay pool]
I --> J[Count events per relay/kind/pubkey]
J --> K[Log relay interactions CLOSED/NOTICE/errors]
K --> L[Periodic checkpoint every 5 min]
L --> H
H -->|Yes| M[Write summary report]
M --> N[Write per-relay JSON stats]
N --> O[Cleanup and exit]
```
## Files to Create
| File | Purpose |
|------|---------|
| [`caching/src/cache_all_test.h`](caching/src/cache_all_test.h) | Header with public API |
| [`caching/src/cache_all_test.c`](caching/src/cache_all_test.c) | Main implementation (~400-500 lines) |
## Files to Modify
| File | Change |
|------|--------|
| [`caching/Makefile`](caching/Makefile) | Add `cache_all_test` target |
## Files NOT Modified
The existing caching daemon files are **not touched**:
- `caching/src/main.c` — unchanged
- `caching/src/backfill.c` — unchanged
- `caching/src/live_subscriber.c` — unchanged
- `caching/src/pg_inbox.c` — unchanged
- `caching/src/forward_catchup.c` — unchanged
- `caching/src/pg_config.c` — unchanged
+331
View File
@@ -0,0 +1,331 @@
# Event Cleanup Page — Design Plan
## Overview
A dedicated admin page for creating, previewing, saving, and executing event deletion queries. Users can filter by follows/non-follows (from caching data), event kinds, and event age, see estimated data savings, and save named queries for reuse.
---
## 1. New Database Table
A new table to persist named cleanup queries:
```sql
CREATE TABLE IF NOT EXISTS cleanup_saved_queries (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
follows_filter TEXT NOT NULL DEFAULT 'all'
CHECK (follows_filter IN ('all', 'follows', 'non_follows')),
kinds INTEGER[] NOT NULL DEFAULT '{}',
max_age_days INTEGER NOT NULL DEFAULT 0,
max_events INTEGER NOT NULL DEFAULT 0,
last_preview_count INTEGER NOT NULL DEFAULT 0,
last_preview_size_bytes BIGINT NOT NULL DEFAULT 0,
last_executed_at BIGINT NOT NULL DEFAULT 0,
created_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT,
updated_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT
);
```
- `kinds` — PostgreSQL integer array. Empty array `{}` means "all kinds".
- `max_age_days` — 0 means "no age filter".
- `max_events` — 0 means "no limit". Used to cap DELETE in batches.
- `follows_filter``'all'` = no follow restriction, `'follows'` = only events from pubkeys in `caching_followed_pubkeys`, `'non_follows'` = only events from pubkeys NOT in `caching_followed_pubkeys`.
---
## 2. New API Endpoints
### 2a. `admin/api/cleanup.php` — Preview & Execute
**GET** — Preview a cleanup query (returns count + size estimate without deleting).
Query parameters:
- `follows_filter``all`, `follows`, `non_follows`
- `kinds` — comma-separated kind numbers (empty = all)
- `max_age_days` — integer
- `max_events` — integer (0 = no limit)
Response:
```json
{
"match_count": 45231,
"total_size_bytes": 52428800,
"total_size_human": "50.0 MB",
"avg_size_per_event": 1159,
"kinds_breakdown": [
{"kind": 1, "count": 30000, "size_bytes": 35000000},
{"kind": 7, "count": 10000, "size_bytes": 10000000}
],
"sql_preview": "SELECT ..."
}
```
**POST** — Execute a saved cleanup query (by ID) or an ad-hoc query.
Body:
```json
{
"query_id": 1,
"dry_run": true
}
```
Or for ad-hoc:
```json
{
"follows_filter": "non_follows",
"kinds": [1, 7],
"max_age_days": 90,
"max_events": 10000,
"dry_run": true
}
```
If `dry_run: true`, returns preview only (same as GET). If `dry_run: false`, executes the DELETE and returns:
```json
{
"deleted_count": 45231,
"freed_bytes": 52428800,
"freed_human": "50.0 MB",
"duration_ms": 1234
}
```
### 2b. `admin/api/cleanup_queries.php` — Saved Query CRUD
**GET** — List all saved queries:
```json
{
"queries": [
{
"id": 1,
"name": "Old non-follow kind 1",
"follows_filter": "non_follows",
"kinds": [1],
"max_age_days": 90,
"max_events": 0,
"last_preview_count": 45231,
"last_preview_size_human": "50.0 MB",
"last_executed_at": null,
"created_at": "2026-08-01 12:00:00"
}
]
}
```
**POST** — Create or update a saved query:
```json
{
"action": "save",
"id": null,
"name": "Old non-follow kind 1",
"follows_filter": "non_follows",
"kinds": [1, 7],
"max_age_days": 90,
"max_events": 0
}
```
**POST with `action: delete`** — Delete a saved query by ID.
**POST with `action: execute`** — Execute a saved query (non-dry-run):
```json
{
"action": "execute",
"id": 1
}
```
---
## 3. SQL Query Construction
The core SQL pattern for preview:
```sql
SELECT COUNT(*) AS match_count,
COALESCE(SUM(pg_column_size(event_json)), 0) AS total_size_bytes
FROM events e
WHERE 1=1
[AND e.kind = ANY(:kinds)] -- if kinds is non-empty
[AND e.created_at < EXTRACT(EPOCH FROM NOW())::BIGINT - :max_age_days * 86400] -- if max_age_days > 0
[AND e.pubkey IN (SELECT pubkey FROM caching_followed_pubkeys)] -- if follows_filter = 'follows'
[AND e.pubkey NOT IN (SELECT pubkey FROM caching_followed_pubkeys)] -- if follows_filter = 'non_follows'
```
For the DELETE:
```sql
DELETE FROM events e
WHERE e.id IN (
SELECT e2.id FROM events e2
WHERE 1=1
[same filters as above]
ORDER BY e2.created_at ASC -- delete oldest first
LIMIT :max_events -- if max_events > 0
)
```
Using `id IN (subquery)` avoids issues with `ORDER BY` + `LIMIT` in DELETE directly, and also lets us count affected rows.
For the kind breakdown in preview:
```sql
SELECT e.kind,
COUNT(*) AS count,
COALESCE(SUM(pg_column_size(e.event_json)), 0) AS size_bytes
FROM events e
WHERE 1=1
[same filters]
GROUP BY e.kind
ORDER BY count DESC
```
---
## 4. UI Design
### 4a. Navigation
Add a new nav item to the side menu in [`admin/index.php`](admin/index.php:51):
```html
<li><button class="nav-item" data-page="cleanup">Cleanup</button></li>
```
### 4b. Page Layout
The cleanup page has two main sections:
#### Section 1: Saved Queries (top)
- Table listing all saved queries with columns: Name, Filters summary, Last preview, Last executed, Actions
- Actions per row: Preview, Execute, Delete
- "New Query" button
#### Section 2: Query Builder / Results (bottom)
When creating a new query or editing an existing one:
**Filter Controls:**
- **Name** — text input
- **Follows filter** — radio buttons: All / Follows only / Non-follows only
- **Kinds** — multi-select or comma-separated text input (with helper showing common kinds: 0, 1, 3, 4, 5, 6, 7, 9734, 9735, 10002, etc.)
- **Max age (days)** — number input (0 = no limit)
- **Max events** — number input (0 = no limit)
**Action Buttons:**
- **Preview** — runs the SELECT preview, shows results below
- **Save Query** — saves the current filter configuration
- **Execute Delete** — runs the actual DELETE (with confirmation dialog)
**Results Area:**
- Match count with human-readable number
- Estimated size freed (with human-readable format)
- Kind breakdown table (kind, count, size)
- SQL preview (collapsible)
- Execution status (after actual delete)
### 4c. Confirmation Dialog
Before executing a DELETE, show a modal with:
- Query name or "Ad-hoc query"
- Number of events that will be deleted
- Estimated space freed
- "This action cannot be undone" warning
- Confirm / Cancel buttons
---
## 5. Files to Create/Modify
### New Files:
| File | Purpose |
|------|---------|
| [`admin/api/cleanup.php`](admin/api/cleanup.php) | Preview + execute cleanup queries |
| [`admin/api/cleanup_queries.php`](admin/api/cleanup_queries.php) | CRUD for saved cleanup queries |
### Modified Files:
| File | Changes |
|------|---------|
| [`admin/index.php`](admin/index.php:51) | Add "Cleanup" nav item + cleanup page HTML section |
| [`admin/assets/app.js`](admin/assets/app.js:52) | Add `cleanup` to page map, loaders, and nav handlers |
| [`src/pg_schema.h`](src/pg_schema.h) | Add `cleanup_saved_queries` table definition |
---
## 6. Implementation Order
1. **Add DB table** — Add `cleanup_saved_queries` to [`src/pg_schema.h`](src/pg_schema.h)
2. **Create `admin/api/cleanup.php`** — Preview (SELECT) and Execute (DELETE) endpoints
3. **Create `admin/api/cleanup_queries.php`** — CRUD for saved queries
4. **Modify `admin/index.php`** — Add nav item + cleanup page HTML
5. **Modify `admin/assets/app.js`** — Add cleanup page controller logic
6. **Test** — Verify preview counts match actual deletes, verify saved query persistence
---
## 7. Mermaid Diagram: User Workflow
```mermaid
flowchart TD
A[Open Cleanup Page] --> B{Has saved queries?}
B -->|Yes| C[Show saved queries list]
B -->|No| D[Show empty state with New Query button]
C --> E[Click New Query or Edit]
D --> E
E --> F[Configure filters: follows, kinds, age, limit]
F --> G[Click Preview]
G --> H[Run SELECT COUNT + size query]
H --> I[Show preview results: count, size, kind breakdown]
I --> J{User action}
J -->|Save| K[Save named query to DB]
J -->|Execute| L[Show confirmation dialog]
L --> M[Run DELETE query]
M --> N[Show execution results]
K --> C
J -->|Adjust filters| F
```
---
## 8. Mermaid Diagram: API Flow
```mermaid
sequenceDiagram
participant U as Browser UI
participant C as cleanup.php
participant Q as cleanup_queries.php
participant DB as PostgreSQL
U->>Q: GET / list saved queries
Q->>DB: SELECT FROM cleanup_saved_queries
DB-->>Q: query list
Q-->>U: JSON queries array
U->>C: GET / preview with filters
C->>DB: SELECT COUNT + SUM pg_column_size
C->>DB: SELECT kind breakdown
DB-->>C: preview data
C-->>U: JSON match_count + size
U->>Q: POST / save query
Q->>DB: INSERT INTO cleanup_saved_queries
DB-->>Q: ok
Q-->>U: JSON saved query
U->>C: POST / execute dry_run=false
C->>DB: DELETE ... WHERE id IN subquery
DB-->>C: deleted count
C-->>U: JSON deleted_count + freed_bytes
```
---
## 9. Edge Cases & Considerations
- **Large deletes**: Use `max_events` to cap deletes. For very large sets, execute in batches.
- **Cascading deletes**: The `event_tags` table has `ON DELETE CASCADE` from events, so tag rows are cleaned up automatically. The `profiles` table has a trigger-based delete for kind-0 events.
- **Concurrent access**: DELETE acquires row locks. For large deletes, consider batching (e.g., 10k at a time).
- **Empty kinds array**: Means "all kinds" — no kind filter applied.
- **Follows filter + no caching data**: If `caching_followed_pubkeys` is empty, `follows` returns 0 matches, `non_follows` returns all events.
- **Size estimation**: Using `pg_column_size(event_json)` gives a good estimate. The `event_json` column stores the full event JSON, which dominates storage. Indexes and TOAST are not counted, but this is fine for a rough estimate.
- **Saved query updates**: If a user saves with the same name, update the existing row (UPSERT on name).
+71
View File
@@ -0,0 +1,71 @@
# Plan: Prevent Direct `make` Usage — Always Use `build_static.sh`
## Problem
An agent ran `make` instead of `./build_static.sh`, which produced a dynamically-linked binary with SQLite backend instead of the static PostgreSQL binary. This caused the production relay to silently fall back to SQLite while the admin UI connected to PostgreSQL.
## Solution
Modify the Makefile so that the relay build targets refuse to run directly and instruct the user to use `build_static.sh` instead. The Makefile will still handle submodule builds (`nostr_core_lib`, `c_utils_lib`) and utility targets.
## Changes
### 1. [`Makefile`](../Makefile) — Guard the relay build targets
Replace the `all`, `$(TARGET)`, `x86`, and `arm64` targets with guards that print an error and exit:
```makefile
# Default target — refuse direct build, instruct to use build_static.sh
all:
@echo "============================================"
@echo " ERROR: Do not run 'make' directly!"
@echo ""
@echo " This project requires a static MUSL build"
@echo " with PostgreSQL backend. Run:"
@echo ""
@echo " ./build_static.sh"
@echo ""
@echo " Or use the full build+restart script:"
@echo ""
@echo " ./make_and_restart_relay.sh"
@echo ""
@echo " The Makefile is only for submodule builds"
@echo " (nostr_core_lib, c_utils_lib) and utility"
@echo " targets (clean, install-deps, etc.)."
@echo "============================================"
@exit 1
```
Keep the submodule build targets (`$(NOSTR_CORE_LIB)`, `$(C_UTILS_LIB)`) and utility targets (`clean`, `install-deps`, `install-arm64-deps`, `force-version`) as-is since they're harmless and useful.
### 2. [`AGENTS.md`](../AGENTS.md) — Reinforce the rule
Add a prominent section at the top of AGENTS.md:
```markdown
## CRITICAL: Never Run `make` Directly
**NEVER run `make` to build the relay binary.** The Makefile will refuse and
print an error. Always use:
- `./build_static.sh` — Build the static MUSL binary with PostgreSQL backend
- `./make_and_restart_relay.sh` — Build, kill old relay, and start new one
The Makefile exists only for submodule compilation (nostr_core_lib, c_utils_lib)
and utility targets (clean, install-deps). Running `make` directly produces a
dynamically-linked binary that will silently fall back to SQLite storage while
the admin UI connects to PostgreSQL — causing the admin page to show stale data.
```
### 3. [`build_static.sh`](../build_static.sh) — Already updated
Already done in the previous round — removed `--db-backend` option, hardcoded PostgreSQL.
### 4. [`make_and_restart_relay.sh`](../make_and_restart_relay.sh) — Already updated
Already done in the previous round — removed `--db-backend` option, always calls `./build_static.sh`.
## Execution Order
1. Update [`Makefile`](../Makefile) — guard `all`, `$(TARGET)`, `x86`, `arm64` targets
2. Update [`AGENTS.md`](../AGENTS.md) — add prominent warning section
+113
View File
@@ -0,0 +1,113 @@
# Plan: Remove SQLite Support — PostgreSQL Only
## Rationale
The project is called `c-relay-pg` and should only support PostgreSQL. The SQLite fallback caused a production issue where the relay was built without PostgreSQL support and silently fell back to SQLite, while the admin UI connected to PostgreSQL — resulting in the admin showing stale data.
## Files to Modify
### 1. [`Makefile`](../Makefile)
| Change | Detail |
|--------|--------|
| Remove `DB_BACKEND ?= sqlite` (line 8) | Hardcode PostgreSQL backend |
| Remove `ifeq/else/endif` conditional (lines 17-23) | Always compile with `-DDB_BACKEND_POSTGRES -DHAVE_LIBPQ` and link `-lpq` |
| Remove `src/db_ops_sqlite.c` from `DB_OPS_SRC` | Only compile `src/db_ops_postgres.c` |
| Remove `-lsqlite3` from `LIBS` (line 6) | No longer needed |
| Remove ARM64 sqlite3 dev references (lines 116, 119, 145, 191) | Replace with postgresql-dev equivalents |
### 2. [`Dockerfile.alpine-musl`](../Dockerfile.alpine-musl)
| Change | Detail |
|--------|--------|
| Remove `ARG DB_BACKEND=sqlite` (line 5) | Hardcode PostgreSQL |
| Remove `ARG DB_BACKEND=sqlite` in builder stage (line 11) | Hardcode PostgreSQL |
| Remove `sqlite-dev` and `sqlite-static` from apk add (lines 29-30) | Keep `postgresql-dev` |
| Remove `if [ "$DB_BACKEND" = "postgres" ]` conditional (lines 118-126) | Always use PostgreSQL flags |
| Remove `DB_LIBS=""` else branch (line 123-125) | Always link `-lpq -lpgcommon -lpgport` |
| Remove `-lsqlite3` from link line (line 139) | No longer needed |
### 3. [`build_static.sh`](../build_static.sh)
| Change | Detail |
|--------|--------|
| Remove `DB_BACKEND="${DB_BACKEND:-postgres}"` (line 14) | Hardcode PostgreSQL |
| Remove `--db-backend` argument parsing (lines 22-33) | No longer needed |
| Remove `sqlite` validation (lines 42-45) | No longer needed |
| Remove `echo "DB backend: $DB_BACKEND"` (line 59) | No longer needed |
| Remove `--build-arg DB_BACKEND=$DB_BACKEND` (lines 158, 182) | No longer needed |
| Update usage message (line 36) | Remove `--db-backend` reference |
### 4. [`make_and_restart_relay.sh`](../make_and_restart_relay.sh)
| Change | Detail |
|--------|--------|
| Remove `DB_BACKEND="postgres"` (line 18) | Hardcode PostgreSQL |
| Remove `--db-backend` argument parsing (lines 138-150) | No longer needed |
| Remove `sqlite` validation (lines 262-266) | No longer needed |
| Remove `if [ "$DB_BACKEND" = "postgres" ]` conditional (line 276) | Always use PostgreSQL path |
| Remove `--db-backend` from help text (line 365) | No longer needed |
| Remove `DB_BACKEND` from build call (line 429) | `./build_static.sh` without args |
### 5. [`src/db_ops.c`](../src/db_ops.c)
| Change | Detail |
|--------|--------|
| Remove `#include "sqlite_db_ops.h"` (line 4) | No longer needed |
| Remove `#ifdef DB_BACKEND_POSTGRES` / `#else` / `#endif` conditional | Always use PostgreSQL dispatch |
| Remove the `#else` block (lines 207-376) that delegates to `sqlite_db_*` functions | Dead code |
### 6. [`src/main.c`](../src/main.c)
| Change | Detail |
|--------|--------|
| Remove SQLite-specific config references (lines 916-931) | Remove `sqlite_mmap_size` and `sqlite_cache_size_kb` PRAGMA setup |
| Remove `sqlite3_open()` comment (line 791) | No longer relevant |
### 7. [`src/config.c`](../src/config.c)
| Change | Detail |
|--------|--------|
| Remove `sqlite_mmap_size` validation (lines 1118-1125) | PostgreSQL-only |
| Remove `sqlite_cache_size_kb` validation (lines 1126-1130) | PostgreSQL-only |
| Remove `sqlite_mmap_size` and `sqlite_cache_size_kb` from integer type list (lines 2178-2179) | PostgreSQL-only |
| Remove `sqlite_mmap_size` and `sqlite_cache_size_kb` from restart-required list (lines 5341-5342) | PostgreSQL-only |
### 8. Source files to remove entirely
| File | Reason |
|------|--------|
| [`src/db_ops_sqlite.c`](../src/db_ops_sqlite.c) | Entire SQLite database implementation |
| [`src/sqlite_db_ops.h`](../src/sqlite_db_ops.h) | SQLite header |
### 9. Test scripts — update sqlite3 CLI references
| File | Change |
|------|--------|
| [`tests/1_nip_test.sh`](../tests/1_nip_test.sh) (lines 444-470) | Replace `sqlite3` queries with `psql` equivalents |
| [`tests/45_nip_test.sh`](../tests/45_nip_test.sh) (lines 428-452) | Replace `sqlite3` queries with `psql` equivalents |
| [`tests/bulk_retrieval_test.sh`](../tests/bulk_retrieval_test.sh) (lines 43-45, 256-258) | Replace `sqlite3` queries with `psql` equivalents |
| [`tests/subscription_cleanup_test.sh`](../tests/subscription_cleanup_test.sh) (lines 91-93, 262-271) | Replace `sqlite3` queries with `psql` equivalents |
| [`tests/large_event_test.sh`](../tests/large_event_test.sh) (line 63) | Update comment |
| [`tests/sql_injection_tests.sh`](../tests/sql_injection_tests.sh) (lines 111, 209-210) | Update SQLite-specific injection strings |
### 10. Example scripts — update sqlite3 references
| File | Change |
|------|--------|
| [`examples/deployment/simple-vps/deploy.sh`](../examples/deployment/simple-vps/deploy.sh) (lines 63, 68) | Replace `sqlite3` with `postgresql` in package lists |
| [`examples/deployment/monitoring/monitor-relay.sh`](../examples/deployment/monitoring/monitor-relay.sh) (lines 249-251, 294-296) | Replace `sqlite3` queries with `psql` |
| [`examples/deployment/backup/backup-relay.sh`](../examples/deployment/backup/backup-relay.sh) (lines 112-115, 187-189, 205-207) | Replace `sqlite3` backup with `pg_dump` |
## Execution Order
1. Remove source files: `src/db_ops_sqlite.c`, `src/sqlite_db_ops.h`
2. Update `src/db_ops.c` — remove SQLite dispatch, always use PostgreSQL
3. Update `src/main.c` — remove SQLite PRAGMA config
4. Update `src/config.c` — remove SQLite config keys
5. Update `Makefile` — hardcode PostgreSQL backend
6. Update `Dockerfile.alpine-musl` — hardcode PostgreSQL backend
7. Update `build_static.sh` — remove `--db-backend` option
8. Update `make_and_restart_relay.sh` — remove `--db-backend` option
9. Update test scripts — replace `sqlite3` with `psql`
10. Update example scripts — replace `sqlite3` with `psql`/`pg_dump`
+70
View File
@@ -0,0 +1,70 @@
# Plan: Fix Missing Statistics Metrics
## Root Causes
### 1. WebSocket Connections = 0
The stats API at [`admin/api/stats.php:33`](admin/api/stats.php:33) queries:
```php
$ws_connections = intval($pdo->query("SELECT count(*) FROM pg_stat_activity
WHERE state = 'active' AND pid != pg_backend_pid()")->fetchColumn());
```
This is **wrong** — WebSocket connections are tracked in-memory by the relay via `g_connection_count` in [`src/websockets.c:139`](src/websockets.c:139), not in `pg_stat_activity`. The PHP code is counting PostgreSQL backend processes, not WebSocket clients.
**Fix:** Query the `subscriptions` table for distinct `wsi_pointer` values where `event_type = 'created'` and no corresponding `'closed'`/`'disconnected'` event exists. Or simpler: count distinct `wsi_pointer` values from the most recent `'created'` entries.
### 2. Active Subscriptions = 0
The stats API at [`admin/api/stats.php:39`](admin/api/stats.php:39) queries:
```php
$active_subscriptions = intval($pdo->query("SELECT count(*) FROM subscriptions
WHERE active = true")->fetchColumn());
```
The `subscriptions` table has **no `active` column** (see [`src/pg_schema.sql:207`](src/pg_schema.sql:207)). It has `event_type` with values 'created', 'closed', 'expired', 'disconnected'.
**Fix:** Count subscriptions where `event_type = 'created'` and no matching `'closed'`/`'disconnected'` record exists for the same `(subscription_id, wsi_pointer)`.
### 3. Memory Usage = '-'
The code at [`admin/api/stats.php:46-51`](admin/api/stats.php:46) reads `/proc/meminfo` — this may fail if the PHP-FPM process is running under a restricted user (e.g., `www-data` with `ProtectProc=invisible` or similar systemd sandboxing).
**Fix:** Use `file_get_contents('/proc/meminfo')` with proper error handling (already there), or fall back to `sys_getloadavg()` and `shell_exec('free -b')` as alternatives.
### 4. Oldest/Newest Event = '-'
The query at [`admin/api/stats.php:63-64`](admin/api/stats.php:63) uses `to_timestamp(MIN(created_at))` — this should work on 3.3M events but may be slow. Could be timing out.
**Fix:** Use `SELECT MIN(created_at), MAX(created_at) FROM events` in a single query, then format in PHP. Add a query timeout safeguard.
## Changes Required
### File: `admin/api/stats.php`
| Line | Current | Fix |
|------|---------|-----|
| 33 | `pg_stat_activity` query | Count distinct active WebSocket connections from `subscriptions` table |
| 39 | `WHERE active = true` | Count active subscriptions using `event_type` logic |
| 46-51 | `/proc/meminfo` | Add fallback using `shell_exec('free -b')` |
| 63-64 | Two separate `to_timestamp` queries | Single `SELECT MIN(created_at), MAX(created_at)` query |
### SQL for Active Connections
```sql
SELECT COUNT(DISTINCT wsi_pointer) FROM subscriptions
WHERE event_type = 'created'
AND wsi_pointer NOT IN (
SELECT wsi_pointer FROM subscriptions
WHERE event_type IN ('closed', 'disconnected', 'expired')
)
```
### SQL for Active Subscriptions
```sql
SELECT COUNT(*) FROM (
SELECT subscription_id, wsi_pointer FROM subscriptions
WHERE event_type = 'created'
EXCEPT
SELECT subscription_id, wsi_pointer FROM subscriptions
WHERE event_type IN ('closed', 'disconnected', 'expired')
) AS active_subs
```
### SQL for Oldest/Newest
```sql
SELECT MIN(created_at) AS oldest, MAX(created_at) AS newest FROM events
```
+189
View File
@@ -0,0 +1,189 @@
# Plan: Publish Kind 1 Status Events to External Relays
## Config Reuse
The config key **`kind_1_status_posts_hours`** already exists in the shared `config` table (used by the main relay at [`src/api.c:975`](src/api.c:975)). The caching service will read the **same** key — no new config needed. Setting it in the admin panel controls both:
- Main relay: generates and stores the kind 1 event locally
- Caching service: publishes it to external upstream relays
## Phase 1: PHP Report Endpoint (`admin/api/kind_1_report.php`)
A new PHP endpoint at `/relay/admin/api/kind_1_report.php` that returns a **markdown-formatted** relay status report. This serves as:
- The content template for the kind 1 event
- A preview page you can visit in the browser to see what will be published
### Report Structure (Markdown)
```
# Relay Name v0.x.x
## Event Rate (Last Hour)
[ASCII chart — same as the 1H chart from chart.php]
## Database Overview
- Total Events: X
- Database Size: X MB
- Oldest Event: date
- Newest Event: date
## Event Kinds (Top 10)
| Kind | Count | % |
|------|-------|---|
| 1 | X | X% |
| 7 | X | X% |
| ... | ... | ... |
## Time-Based Statistics
| Period | Events |
|----------|--------|
| 24 Hours | X |
| 7 Days | X |
| 30 Days | X |
## Top Pubkeys
| # | Name | Pubkey | Events | % |
|---|------|--------|--------|---|
| 1 | ... | ... | X | X%|
| 2 | ... | ... | X | X%|
```
### Implementation
The PHP file will:
1. Query PostgreSQL for all stats (same queries as [`admin/api/stats.php`](admin/api/stats.php))
2. Call the existing chart endpoint to get the 1H ASCII chart
3. Format everything as markdown
4. Return `Content-Type: text/plain; charset=utf-8`
### Nginx Config
Add a location block so the endpoint is accessible:
```nginx
location ^~ /relay/admin/api/kind_1_report.php {
alias /opt/c-relay-pg/admin/api/kind_1_report.php;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index kind_1_report.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $request_filename;
}
```
## Phase 2: Caching Service Publishing
### 2a. `pg_inbox_get_relay_private_key()` (new function in `pg_inbox.c`)
Reads the relay's private key from the `relay_seckey` table. Returns a malloc'd hex string.
### 2b. Status publish tick (new function in `main.c`)
A periodic task in the main loop that:
- Reads `kind_1_status_posts_hours` from the config table
- If enabled and time to publish:
1. Fetches the relay private key from `relay_seckey`
2. Fetches the report text by calling the PHP endpoint via HTTP (or generates it directly via SQL)
3. Creates and signs a kind 1 event using `nostr_create_and_sign_event()`
4. Publishes to all connected upstream relays via `nostr_relay_pool_publish_async()`
## Files to Modify
| File | Change |
|------|--------|
| `admin/api/kind_1_report.php` | **New** — PHP endpoint returning markdown report |
| `caching/src/pg_inbox.h` | Add `pg_inbox_get_relay_private_key()` declaration |
| `caching/src/pg_inbox.c` | Implement `pg_inbox_get_relay_private_key()` |
| `caching/src/main.c` | Add periodic status publish tick |
| nginx config | Add location for kind_1_report.php |
## Current State
The main relay (`c_relay_pg`) already has a [`generate_and_post_status_event()`](src/api.c:973) function that:
1. Generates relay statistics text via [`generate_stats_text()`](src/api.c:2007)
2. Signs it as a **kind 1** event with the relay's private key (from [`relay_seckey`](src/db_ops_postgres.c:1611) table)
3. Stores it locally and broadcasts to connected clients
But it **never publishes to external relays** — the main relay has no WebSocket client capability.
The caching service (`caching_relay`) has a full WebSocket relay pool (`nostr_relay_pool_t`) that can connect to external relays and publish events. It already connects to 26 upstream relays.
## Architecture
```
┌─────────────────────┐ ┌──────────────────────────────┐
│ PHP Stats Endpoint │────▶│ PostgreSQL (stats data) │
│ /relay/api/stats/ │ │ │
└─────────────────────┘ └──────────┬───────────────────┘
│ reads every N hours
┌──────────────────────────────────────────────────────────┐
│ caching_relay (main loop) │
│ │
│ 1. Read relay private key from relay_seckey table │
│ 2. Generate stats text (SQL queries via pg_inbox) │
│ 3. Create + sign kind 1 event │
│ 4. Publish to ALL connected upstream relays │
│ (via nostr_relay_pool_publish_async) │
└──────────────────────────────────────────────────────────┘
```
## Changes Required
### 1. PHP Stats Text Endpoint (new file: `admin/api/stats_text.php`)
A public (or admin-authenticated) PHP endpoint that queries PostgreSQL directly and returns the stats text. This is useful for:
- Manual preview of what the relay publishes
- The caching service could fetch it via HTTP (but we'll use direct SQL instead)
### 2. Caching Service: `pg_inbox_generate_stats_text()` (new function in `pg_inbox.c`)
A C function that queries PostgreSQL to generate the same stats text that the main relay's `generate_stats_text()` produces. It queries:
- `SELECT COUNT(*) FROM events` — total events
- `SELECT COUNT(*) FROM events WHERE created_at > ...` — time-based stats
- `SELECT kind, COUNT(*) FROM events GROUP BY kind` — kind distribution
- `SELECT pubkey, COUNT(*) FROM events GROUP BY pubkey ORDER BY COUNT(*) DESC LIMIT 10` — top pubkeys
- `SELECT pg_database_size('crelay')` — database size
### 3. Caching Service: `pg_inbox_get_relay_private_key()` (new function in `pg_inbox.c`)
Reads the relay's private key from the `relay_seckey` table so the caching service can sign events as the relay.
### 4. Caching Service: Status publish tick (new function in `main.c`)
A periodic task in the main loop that:
- Checks if it's time to publish (configurable interval, e.g., every 6 hours)
- Reads the relay private key from PostgreSQL
- Generates stats text via SQL queries
- Creates and signs a kind 1 event using `nostr_create_and_sign_event()`
- Publishes to all connected upstream relays via `nostr_relay_pool_publish_async()`
### 5. Config: `status_publish_hours` (new config key)
Controls how often the status event is published. Stored in the `config` table. Default 0 = disabled.
## Files to Modify
| File | Change |
|------|--------|
| `admin/api/stats_text.php` | **New** — PHP endpoint returning stats text |
| `caching/src/pg_inbox.h` | Add declarations for `pg_inbox_generate_stats_text()` and `pg_inbox_get_relay_private_key()` |
| `caching/src/pg_inbox.c` | Implement both functions |
| `caching/src/main.c` | Add periodic status publish tick in main loop |
| `caching/src/pg_config.c` | Add `status_publish_hours` to config loading |
## Data Flow
```
Every N hours (config: status_publish_hours):
1. main.c checks if time to publish
2. Calls pg_inbox_generate_stats_text() → returns malloc'd string
3. Calls pg_inbox_get_relay_private_key() → returns hex key
4. Creates cJSON event: kind=1, content=stats_text, tags=[]
5. Signs with nostr_create_and_sign_event(1, content, tags, privkey, now)
6. Publishes via nostr_relay_pool_publish_async() to all upstream relays
7. Free resources
```
## Dependencies
- The caching service already links `nostr_core_lib` which provides `nostr_create_and_sign_event()` and `nostr_relay_pool_publish_async()`
- The caching service already has a PostgreSQL connection via `pg_inbox`
- No new libraries needed
+1 -1
View File
@@ -1 +1 @@
2356597
467323
+27 -5
View File
@@ -977,10 +977,32 @@ int generate_and_post_status_event(void) {
return 0; // Feature disabled
}
// Generate statistics text content using existing function
char* stats_text = generate_stats_text();
if (!stats_text) {
DEBUG_ERROR("Failed to generate statistics text for status post");
// Generate report content by running the PHP kind_1_report.php script.
// This produces a markdown-formatted relay status report.
char* stats_text = NULL;
FILE *fp = popen("php -r 'require \"/opt/c-relay-pg/admin/api/kind_1_report.php\";' 2>/dev/null", "r");
if (fp) {
size_t total = 0, cap = 16384;
stats_text = malloc(cap);
if (stats_text) {
int n;
while ((n = fread(stats_text + total, 1, cap - total - 1, fp)) > 0) {
total += n;
if (total >= cap - 1) {
cap *= 2;
char *tmp = realloc(stats_text, cap);
if (!tmp) { free(stats_text); stats_text = NULL; break; }
stats_text = tmp;
}
}
if (stats_text) stats_text[total] = '\0';
}
pclose(fp);
}
if (!stats_text || strlen(stats_text) == 0) {
DEBUG_ERROR("Failed to generate report via PHP for status post");
free(stats_text);
return -1;
}
@@ -1006,7 +1028,7 @@ int generate_and_post_status_event(void) {
// Create and sign the kind 1 event
cJSON* signed_event = nostr_create_and_sign_event(
1, // kind 1 = text note
stats_text, // content = statistics
stats_text, // content = markdown report
tags, // empty tags
relay_privkey_bytes, // relay's private key
time(NULL) // current timestamp
+1 -21
View File
@@ -1114,22 +1114,6 @@ static int validate_config_field(const char* key, const char* value, char* error
}
return 0;
}
// SQLite performance tuning
if (strcmp(key, "sqlite_mmap_size") == 0) {
if (!is_valid_positive_integer(value) && strcmp(value, "0") != 0) {
snprintf(error_msg, error_size, "invalid sqlite_mmap_size '%s' (must be non-negative integer bytes)", value);
return -1;
}
return 0;
}
if (strcmp(key, "sqlite_cache_size_kb") == 0) {
if (!is_valid_positive_integer(value)) {
snprintf(error_msg, error_size, "invalid sqlite_cache_size_kb '%s' (must be positive integer KB)", value);
return -1;
}
return 0;
}
// Boolean fields
if (strcmp(key, "auth_enabled") == 0 ||
@@ -2174,9 +2158,7 @@ int populate_default_config_values(void) {
strcmp(key, "default_limit") == 0 ||
strcmp(key, "max_limit") == 0 ||
strcmp(key, "nip42_challenge_expiration") == 0 ||
strcmp(key, "nip40_expiration_grace_period") == 0 ||
strcmp(key, "sqlite_mmap_size") == 0 ||
strcmp(key, "sqlite_cache_size_kb") == 0) {
strcmp(key, "nip40_expiration_grace_period") == 0) {
data_type = "integer";
} else if (strcmp(key, "auth_enabled") == 0 ||
strcmp(key, "nip40_expiration_enabled") == 0 ||
@@ -5338,8 +5320,6 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
strcmp(key, "max_limit") == 0 ||
strcmp(key, "nip42_challenge_expiration") == 0 ||
strcmp(key, "nip40_expiration_grace_period") == 0 ||
strcmp(key, "sqlite_mmap_size") == 0 ||
strcmp(key, "sqlite_cache_size_kb") == 0 ||
strcmp(key, "caching_config_generation") == 0 ||
strcmp(key, "caching_live_resubscribe_seconds") == 0 ||
strcmp(key, "caching_backfill_page_size") == 0 ||
-226
View File
@@ -1,11 +1,8 @@
#define _GNU_SOURCE
#include "db_ops.h"
#include "sqlite_db_ops.h"
#include "db_ops_postgres.h"
#ifdef DB_BACKEND_POSTGRES
int db_init(const char* connection_string) { return postgres_db_init(connection_string); }
void db_close(void) { postgres_db_close(); }
int db_is_available(void) { return postgres_db_is_available(); }
@@ -202,226 +199,3 @@ cJSON* db_get_profile(const char* pubkey) {
cJSON* db_get_profiles(const char** pubkeys, int count) {
return postgres_db_get_profiles(pubkeys, count);
}
#else
int db_init(const char* connection_string) { return sqlite_db_init(connection_string); }
void db_close(void) { sqlite_db_close(); }
int db_is_available(void) { return sqlite_db_is_available(); }
const char* db_last_error(void) { return sqlite_db_last_error(); }
const char* db_get_database_path(void) { return sqlite_db_get_database_path(); }
int db_set_thread_connection(void* connection) { return sqlite_db_set_thread_connection(connection); }
void db_clear_thread_connection(void) { sqlite_db_clear_thread_connection(); }
int db_open_worker_connection(const char* db_path, void** out_connection) {
return sqlite_db_open_worker_connection(db_path, out_connection);
}
void db_close_worker_connection(void* connection) { sqlite_db_close_worker_connection(connection); }
int db_worker_listen(void* connection, const char* channel) {
return sqlite_db_worker_listen(connection, channel);
}
int db_worker_poll_notify(void* connection, int timeout_ms) {
return sqlite_db_worker_poll_notify(connection, timeout_ms);
}
int db_worker_poll_notify_with_payload(void* connection, int timeout_ms,
char** out_channel, char** out_payload) {
(void)connection; (void)timeout_ms;
if (out_channel) *out_channel = NULL;
if (out_payload) *out_payload = NULL;
return -1; /* SQLite has no LISTEN/NOTIFY */
}
int db_prepare(const char* sql, db_stmt_t** out_stmt) {
return sqlite_db_prepare(sql, (sqlite_db_stmt_t**)out_stmt);
}
int db_bind_text_param(db_stmt_t* stmt, int index, const char* value) {
return sqlite_db_bind_text_param((sqlite_db_stmt_t*)stmt, index, value);
}
int db_bind_int_param(db_stmt_t* stmt, int index, int value) {
return sqlite_db_bind_int_param((sqlite_db_stmt_t*)stmt, index, value);
}
int db_bind_int64_param(db_stmt_t* stmt, int index, long long value) {
return sqlite_db_bind_int64_param((sqlite_db_stmt_t*)stmt, index, value);
}
int db_step_stmt(db_stmt_t* stmt) { return sqlite_db_step_stmt((sqlite_db_stmt_t*)stmt); }
int db_reset_stmt(db_stmt_t* stmt) { return sqlite_db_reset_stmt((sqlite_db_stmt_t*)stmt); }
const char* db_column_text_value(db_stmt_t* stmt, int col) {
return sqlite_db_column_text_value((sqlite_db_stmt_t*)stmt, col);
}
int db_column_int_value(db_stmt_t* stmt, int col) {
return sqlite_db_column_int_value((sqlite_db_stmt_t*)stmt, col);
}
long long db_column_int64_value(db_stmt_t* stmt, int col) {
return sqlite_db_column_int64_value((sqlite_db_stmt_t*)stmt, col);
}
double db_column_double_value(db_stmt_t* stmt, int col) {
return sqlite_db_column_double_value((sqlite_db_stmt_t*)stmt, col);
}
void db_finalize_stmt(db_stmt_t* stmt) { sqlite_db_finalize_stmt((sqlite_db_stmt_t*)stmt); }
int db_log_subscription_created(const char* sub_id, const char* wsi_ptr,
const char* client_ip, const char* filter_json) {
return sqlite_db_log_subscription_created(sub_id, wsi_ptr, client_ip, filter_json);
}
int db_log_subscription_closed(const char* sub_id, const char* client_ip) {
return sqlite_db_log_subscription_closed(sub_id, client_ip);
}
int db_log_subscription_disconnected(const char* client_ip) {
return sqlite_db_log_subscription_disconnected(client_ip);
}
int db_update_subscription_events_sent(const char* sub_id, int events_sent) {
return sqlite_db_update_subscription_events_sent(sub_id, events_sent);
}
int db_cleanup_orphaned_subscriptions(void) { return sqlite_db_cleanup_orphaned_subscriptions(); }
int db_get_event_pubkey(const char* event_id, char* pubkey_out, size_t pubkey_out_size) {
return sqlite_db_get_event_pubkey(event_id, pubkey_out, pubkey_out_size);
}
int db_delete_event_by_id(const char* event_id, const char* requester_pubkey) {
return sqlite_db_delete_event_by_id(event_id, requester_pubkey);
}
int db_delete_events_by_address(const char* pubkey, int kind, const char* d_tag, long before_timestamp) {
return sqlite_db_delete_events_by_address(pubkey, kind, d_tag, before_timestamp);
}
int db_is_pubkey_blacklisted(const char* pubkey) { return sqlite_db_is_pubkey_blacklisted(pubkey); }
int db_is_hash_blacklisted(const char* resource_hash) { return sqlite_db_is_hash_blacklisted(resource_hash); }
int db_is_pubkey_whitelisted(const char* pubkey) { return sqlite_db_is_pubkey_whitelisted(pubkey); }
int db_count_active_whitelist_rules(void) { return sqlite_db_count_active_whitelist_rules(); }
int db_count_with_sql(const char* sql, const char** bind_params, int bind_param_count, int* out_count) {
return sqlite_db_count_with_sql(sql, bind_params, bind_param_count, out_count);
}
char* db_execute_readonly_query_json(const char* query, const char* request_id,
char* error_message, size_t error_size,
int max_rows, int timeout_ms) {
return sqlite_db_execute_readonly_query_json(query, request_id, error_message, error_size,
max_rows, timeout_ms);
}
int db_get_total_event_count_ll(long long* out_count) { return sqlite_db_get_total_event_count_ll(out_count); }
int db_get_event_count_since(time_t cutoff, long long* out_count) {
return sqlite_db_get_event_count_since(cutoff, out_count);
}
int db_get_storage_size_bytes(long long* out_size) {
return sqlite_db_get_storage_size_bytes(out_size);
}
cJSON* db_get_event_kind_distribution_rows(long long* out_total_events) {
return sqlite_db_get_event_kind_distribution_rows(out_total_events);
}
cJSON* db_get_top_pubkeys_rows(int limit) { return sqlite_db_get_top_pubkeys_rows(limit); }
cJSON* db_get_subscription_details_rows(void) { return sqlite_db_get_subscription_details_rows(); }
cJSON* db_get_all_config_rows(void) { return sqlite_db_get_all_config_rows(); }
char* db_get_config_value_dup(const char* key) { return sqlite_db_get_config_value_dup(key); }
int db_set_config_value_full(const char* key, const char* value, const char* data_type,
const char* description, const char* category, int requires_restart) {
return sqlite_db_set_config_value_full(key, value, data_type, description, category, requires_restart);
}
int db_update_config_value_only(const char* key, const char* value) {
return sqlite_db_update_config_value_only(key, value);
}
int db_upsert_config_value(const char* key, const char* value, const char* data_type) {
return sqlite_db_upsert_config_value(key, value, data_type);
}
int db_store_relay_private_key_hex(const char* relay_privkey_hex) {
return sqlite_db_store_relay_private_key_hex(relay_privkey_hex);
}
char* db_get_relay_private_key_hex_dup(void) { return sqlite_db_get_relay_private_key_hex_dup(); }
int db_store_config_event(const cJSON* event) { return sqlite_db_store_config_event(event); }
int db_insert_event_with_json(const char* id, const char* pubkey, long long created_at,
int kind, const char* event_type, const char* content,
const char* sig, const char* tags_json, const char* event_json,
int* out_step_rc, int* out_extended_errcode) {
return sqlite_db_insert_event_with_json(id, pubkey, created_at, kind, event_type, content,
sig, tags_json, event_json, out_step_rc, out_extended_errcode);
}
int db_get_event_time_bounds(long long* out_min_created_at, long long* out_max_created_at) {
return sqlite_db_get_event_time_bounds(out_min_created_at, out_max_created_at);
}
int db_event_id_exists(const char* event_id, int* out_exists) {
return sqlite_db_event_id_exists(event_id, out_exists);
}
cJSON* db_retrieve_event_by_id(const char* event_id) { return sqlite_db_retrieve_event_by_id(event_id); }
char* db_get_latest_event_pubkey_for_kind_dup(int kind) {
return sqlite_db_get_latest_event_pubkey_for_kind_dup(kind);
}
int db_get_config_row_count(int* out_count) { return sqlite_db_get_config_row_count(out_count); }
int db_store_event_tags_cjson(const char* event_id, const cJSON* tags) {
return sqlite_db_store_event_tags_cjson(event_id, tags);
}
int db_populate_event_tags_from_existing(void) { return sqlite_db_populate_event_tags_from_existing(); }
int db_add_auth_rule(const char* rule_type, const char* pattern_type, const char* pattern_value) {
return sqlite_db_add_auth_rule(rule_type, pattern_type, pattern_value);
}
int db_remove_auth_rule(const char* rule_type, const char* pattern_type, const char* pattern_value) {
return sqlite_db_remove_auth_rule(rule_type, pattern_type, pattern_value);
}
int db_delete_wot_whitelist_rules(void) { return sqlite_db_delete_wot_whitelist_rules(); }
int db_count_wot_whitelist_rules(void) { return sqlite_db_count_wot_whitelist_rules(); }
int db_table_exists(const char* table_name, int* out_exists) {
return sqlite_db_table_exists(table_name, out_exists);
}
char* db_get_schema_version_dup(void) { return sqlite_db_get_schema_version_dup(); }
int db_exec_sql(const char* sql) { return sqlite_db_exec_sql(sql); }
int db_wal_checkpoint_passive(void) { return sqlite_db_wal_checkpoint_passive(); }
int db_wal_checkpoint_truncate(void) { return sqlite_db_wal_checkpoint_truncate(); }
// Caching relay inbox helpers are PostgreSQL-only. The SQLite backend has no
// caching_event_inbox table, so the dispatch path returns errors/no-ops here
// rather than delegating to sqlite_db_ops stubs.
cJSON* db_caching_inbox_dequeue_batch(int batch_size, int* out_count) {
if (out_count) *out_count = 0;
(void)batch_size;
return NULL;
}
int db_caching_inbox_pending_counts(int* out_live_count, int* out_backfill_count) {
if (out_live_count) *out_live_count = 0;
if (out_backfill_count) *out_backfill_count = 0;
return DB_ERROR;
}
int db_caching_inbox_oldest_age(int* out_oldest_age_seconds) {
if (out_oldest_age_seconds) *out_oldest_age_seconds = 0;
return DB_ERROR;
}
int db_caching_backfill_author_counts(int* out_complete, int* out_total) {
if (out_complete) *out_complete = 0;
if (out_total) *out_total = 0;
return DB_ERROR;
}
int db_caching_inbox_insert(const char* event_id, const char* event_json,
const char* source_relay, const char* source_class,
int priority) {
(void)event_id; (void)event_json; (void)source_relay; (void)source_class; (void)priority;
return DB_ERROR;
}
cJSON* db_get_profile_metadata(const char* pubkey) {
/* SQLite backend: kind-0 metadata not supported in this context.
* The caching follows feature is PostgreSQL-only. */
(void)pubkey;
return NULL;
}
cJSON* db_get_outbox_relays(const char* pubkey) {
(void)pubkey;
return NULL;
}
cJSON* db_get_profile(const char* pubkey) {
/* SQLite backend: profiles cache table is PostgreSQL-only. */
(void)pubkey;
return NULL;
}
cJSON* db_get_profiles(const char** pubkeys, int count) {
(void)pubkeys;
(void)count;
return NULL;
}
#endif
-1428
View File
File diff suppressed because it is too large Load Diff
+4 -37
View File
@@ -788,7 +788,7 @@ int init_database(const char* database_path_override) {
if (db_get_config_row_count(&row_count) == 0) {
DEBUG_LOG("Config table row count immediately after db_init(): %d", row_count);
} else {
DEBUG_LOG("Config table count unavailable immediately after sqlite3_open() (table may not exist yet)");
DEBUG_LOG("Config table count unavailable immediately after db_init() (table may not exist yet)");
}
}
// DEBUG_GUARD_END
@@ -908,36 +908,11 @@ int init_database(const char* database_path_override) {
DEBUG_WARN("Failed to enable WAL mode");
// Continue anyway - WAL mode is optional
} else {
DEBUG_LOG("SQLite WAL mode enabled");
DEBUG_LOG("WAL mode enabled");
}
// PostgreSQL performance tuning is handled via postgresql.conf.
// No SQLite-specific PRAGMAs are needed.
// Apply SQLite performance tuning PRAGMAs from config
// mmap_size: memory-map the database file to eliminate pread64 syscall overhead
// Default 256MB covers most relay databases; set to 0 to disable
long mmap_size = get_config_int("sqlite_mmap_size", 268435456);
if (mmap_size > 0) {
char mmap_pragma[64];
snprintf(mmap_pragma, sizeof(mmap_pragma), "PRAGMA mmap_size=%ld;", mmap_size);
if (db_exec_sql(mmap_pragma) != 0) {
DEBUG_WARN("Failed to set mmap_size");
} else {
DEBUG_LOG("SQLite mmap_size set to %ld bytes", mmap_size);
}
}
// cache_size_kb: page cache size in KB (negative value = KB, positive = number of 4KB pages)
// Default 64MB keeps hot event data in memory and reduces repeated disk reads
int cache_size_kb = get_config_int("sqlite_cache_size_kb", 65536);
if (cache_size_kb != 0) {
char cache_pragma[64];
// Use negative value so SQLite interprets it as KB rather than page count
snprintf(cache_pragma, sizeof(cache_pragma), "PRAGMA cache_size=-%d;", cache_size_kb > 0 ? cache_size_kb : -cache_size_kb);
if (db_exec_sql(cache_pragma) != 0) {
DEBUG_WARN("Failed to set cache_size");
} else {
DEBUG_LOG("SQLite cache_size set to %d KB", cache_size_kb);
}
}
DEBUG_TRACE("Exiting init_database() - success");
return 0;
@@ -948,14 +923,6 @@ void close_database() {
DEBUG_TRACE("Entering close_database()");
if (db_is_available()) {
#ifndef DB_BACKEND_POSTGRES
// Perform WAL checkpoint to minimize stale files on next startup (SQLite only)
DEBUG_LOG("Performing WAL checkpoint before database close");
if (db_exec_sql("PRAGMA wal_checkpoint(TRUNCATE);") != 0) {
DEBUG_WARN("WAL checkpoint warning");
}
#endif
db_close();
DEBUG_LOG("Database connection closed");
}
+2 -2
View File
@@ -13,8 +13,8 @@
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
#define CRELAY_VERSION_MAJOR 2
#define CRELAY_VERSION_MINOR 1
#define CRELAY_VERSION_PATCH 25
#define CRELAY_VERSION "v2.1.25"
#define CRELAY_VERSION_PATCH 35
#define CRELAY_VERSION "v2.1.35"
// Relay metadata (authoritative source for NIP-11 information)
#define RELAY_NAME "C-Relay-PG"
+19
View File
@@ -743,6 +743,25 @@ static const char* const EMBEDDED_PG_SCHEMA_SQL =
"END\n"
"$$;\n"
"\n"
"-- =====================================================================\n"
"-- Saved cleanup queries for the admin Event Cleanup page.\n"
"-- =====================================================================\n"
"CREATE TABLE IF NOT EXISTS cleanup_saved_queries (\n"
" id BIGSERIAL PRIMARY KEY,\n"
" name TEXT NOT NULL UNIQUE,\n"
" follows_filter TEXT NOT NULL DEFAULT 'all'\n"
" CHECK (follows_filter IN ('all', 'follows', 'non_follows')),\n"
" kinds INTEGER[] NOT NULL DEFAULT '{}',\n"
" from_date TEXT NOT NULL DEFAULT '',\n"
" to_date TEXT NOT NULL DEFAULT '',\n"
" max_events INTEGER NOT NULL DEFAULT 0,\n"
" last_preview_count INTEGER NOT NULL DEFAULT 0,\n"
" last_preview_size_bytes BIGINT NOT NULL DEFAULT 0,\n"
" last_executed_at BIGINT NOT NULL DEFAULT 0,\n"
" created_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT,\n"
" updated_at BIGINT NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW())::BIGINT\n"
");\n"
"\n"
"COMMIT;\n"
;
-99
View File
@@ -1,99 +0,0 @@
#ifndef SQLITE_DB_OPS_H
#define SQLITE_DB_OPS_H
#include "db_ops.h"
typedef struct sqlite_db_stmt sqlite_db_stmt_t;
int sqlite_db_init(const char* connection_string);
void sqlite_db_close(void);
int sqlite_db_is_available(void);
const char* sqlite_db_last_error(void);
const char* sqlite_db_get_database_path(void);
int sqlite_db_set_thread_connection(void* connection);
void sqlite_db_clear_thread_connection(void);
int sqlite_db_open_worker_connection(const char* db_path, void** out_connection);
void sqlite_db_close_worker_connection(void* connection);
int sqlite_db_worker_listen(void* connection, const char* channel);
int sqlite_db_worker_poll_notify(void* connection, int timeout_ms);
int sqlite_db_prepare(const char* sql, sqlite_db_stmt_t** out_stmt);
int sqlite_db_bind_text_param(sqlite_db_stmt_t* stmt, int index, const char* value);
int sqlite_db_bind_int_param(sqlite_db_stmt_t* stmt, int index, int value);
int sqlite_db_bind_int64_param(sqlite_db_stmt_t* stmt, int index, long long value);
int sqlite_db_step_stmt(sqlite_db_stmt_t* stmt);
int sqlite_db_reset_stmt(sqlite_db_stmt_t* stmt);
const char* sqlite_db_column_text_value(sqlite_db_stmt_t* stmt, int col);
int sqlite_db_column_int_value(sqlite_db_stmt_t* stmt, int col);
long long sqlite_db_column_int64_value(sqlite_db_stmt_t* stmt, int col);
double sqlite_db_column_double_value(sqlite_db_stmt_t* stmt, int col);
void sqlite_db_finalize_stmt(sqlite_db_stmt_t* stmt);
int sqlite_db_log_subscription_created(const char* sub_id, const char* wsi_ptr,
const char* client_ip, const char* filter_json);
int sqlite_db_log_subscription_closed(const char* sub_id, const char* client_ip);
int sqlite_db_log_subscription_disconnected(const char* client_ip);
int sqlite_db_update_subscription_events_sent(const char* sub_id, int events_sent);
int sqlite_db_cleanup_orphaned_subscriptions(void);
int sqlite_db_get_event_pubkey(const char* event_id, char* pubkey_out, size_t pubkey_out_size);
int sqlite_db_delete_event_by_id(const char* event_id, const char* requester_pubkey);
int sqlite_db_delete_events_by_address(const char* pubkey, int kind,
const char* d_tag, long before_timestamp);
int sqlite_db_is_pubkey_blacklisted(const char* pubkey);
int sqlite_db_is_hash_blacklisted(const char* resource_hash);
int sqlite_db_is_pubkey_whitelisted(const char* pubkey);
int sqlite_db_count_active_whitelist_rules(void);
int sqlite_db_count_with_sql(const char* sql, const char** bind_params, int bind_param_count, int* out_count);
char* sqlite_db_execute_readonly_query_json(const char* query, const char* request_id,
char* error_message, size_t error_size,
int max_rows, int timeout_ms);
int sqlite_db_get_total_event_count_ll(long long* out_count);
int sqlite_db_get_event_count_since(time_t cutoff, long long* out_count);
int sqlite_db_get_storage_size_bytes(long long* out_size);
cJSON* sqlite_db_get_event_kind_distribution_rows(long long* out_total_events);
cJSON* sqlite_db_get_top_pubkeys_rows(int limit);
cJSON* sqlite_db_get_subscription_details_rows(void);
cJSON* sqlite_db_get_all_config_rows(void);
char* sqlite_db_get_config_value_dup(const char* key);
int sqlite_db_set_config_value_full(const char* key, const char* value, const char* data_type,
const char* description, const char* category, int requires_restart);
int sqlite_db_update_config_value_only(const char* key, const char* value);
int sqlite_db_upsert_config_value(const char* key, const char* value, const char* data_type);
int sqlite_db_store_relay_private_key_hex(const char* relay_privkey_hex);
char* sqlite_db_get_relay_private_key_hex_dup(void);
int sqlite_db_store_config_event(const cJSON* event);
int sqlite_db_insert_event_with_json(const char* id, const char* pubkey, long long created_at,
int kind, const char* event_type, const char* content,
const char* sig, const char* tags_json, const char* event_json,
int* out_step_rc, int* out_extended_errcode);
int sqlite_db_get_event_time_bounds(long long* out_min_created_at, long long* out_max_created_at);
int sqlite_db_event_id_exists(const char* event_id, int* out_exists);
cJSON* sqlite_db_retrieve_event_by_id(const char* event_id);
char* sqlite_db_get_latest_event_pubkey_for_kind_dup(int kind);
int sqlite_db_get_config_row_count(int* out_count);
int sqlite_db_store_event_tags_cjson(const char* event_id, const cJSON* tags);
int sqlite_db_populate_event_tags_from_existing(void);
int sqlite_db_add_auth_rule(const char* rule_type, const char* pattern_type, const char* pattern_value);
int sqlite_db_remove_auth_rule(const char* rule_type, const char* pattern_type, const char* pattern_value);
int sqlite_db_delete_wot_whitelist_rules(void);
int sqlite_db_count_wot_whitelist_rules(void);
int sqlite_db_table_exists(const char* table_name, int* out_exists);
char* sqlite_db_get_schema_version_dup(void);
int sqlite_db_exec_sql(const char* sql);
int sqlite_db_wal_checkpoint_passive(void);
int sqlite_db_wal_checkpoint_truncate(void);
#endif // SQLITE_DB_OPS_H
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=pgweb - PostgreSQL web UI
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User=user
ExecStart=/usr/local/bin/pgweb --url "postgres://crelay:crelay@localhost:5432/crelay?sslmode=disable" --listen 8091
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
+120 -26
View File
@@ -442,32 +442,126 @@ run_comprehensive_test() {
# Check what's actually stored in the database
print_step "Verifying database contents..."
if command -v sqlite3 &> /dev/null; then
# Find the database file (should be in build/ directory with relay pubkey as filename)
local db_file=""
if [[ -d "../build" ]]; then
db_file=$(find ../build -name "*.db" -type f | head -1)
fi
if [[ -n "$db_file" && -f "$db_file" ]]; then
print_info "Events by type in database ($db_file):"
sqlite3 "$db_file" "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null | while read line; do
echo " $line"
done
print_info "Recent events in database:"
sqlite3 "$db_file" "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null | while read line; do
echo " $line"
done
print_success "Database verification complete"
else
print_warning "Database file not found in build/ directory"
print_info "Expected database files: build/*.db (named after relay pubkey)"
fi
else
print_warning "sqlite3 not available for database verification"
fi
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Recent events in database:"
psql -d crelay -c "SELECT substr(id, 1, 16) || '...' as short_id, event_type, kind, substr(content, 1, 30) || '...' as short_content FROM events ORDER BY created_at DESC LIMIT 5;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
return 0
}
+113 -25
View File
@@ -425,31 +425,119 @@ run_count_test() {
# Check what's actually stored in the database
print_step "Verifying database contents..."
if command -v sqlite3 &> /dev/null; then
# Find the database file (should be in build/ directory with relay pubkey as filename)
local db_file=""
if [[ -d "../build" ]]; then
db_file=$(find ../build -name "*.db" -type f | head -1)
fi
if [[ -n "$db_file" && -f "$db_file" ]]; then
print_info "Events by type in database ($db_file):"
sqlite3 "$db_file" "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null | while read line; do
echo " $line"
done
print_info "Total events in database:"
sqlite3 "$db_file" "SELECT COUNT(*) FROM events;" 2>/dev/null
print_success "Database verification complete"
else
print_warning "Database file not found in build/ directory"
print_info "Expected database files: build/*.db (named after relay pubkey)"
fi
else
print_warning "sqlite3 not available for database verification"
fi
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
print_info "Events by type in database:"
psql -d crelay -c "SELECT event_type, COUNT(*) as count FROM events GROUP BY event_type;" 2>/dev/null || print_warning "Could not query database"
print_info "Total events in database:"
psql -d crelay -c "SELECT COUNT(*) FROM events;" 2>/dev/null || print_warning "Could not query database"
print_success "Database verification complete"
return 0
}
+2 -2
View File
@@ -41,7 +41,7 @@ fi
# Check current event count in database
DB_FILE=$(ls build/*.db 2>/dev/null | head -1)
if [ -n "$DB_FILE" ]; then
CURRENT_COUNT=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM events WHERE kind=1;" 2>/dev/null || echo "0")
CURRENT_COUNT=$(psql -d crelay "SELECT COUNT(*) FROM events WHERE kind=1;" 2>/dev/null || echo "0")
echo "Current kind 1 events in database: $CURRENT_COUNT"
if [ "$CURRENT_COUNT" -ge "$NUM_EVENTS" ]; then
@@ -254,7 +254,7 @@ echo " - Per-event overhead: ~0.02ms (vs 50ms before)"
echo ""
if [ -n "$DB_FILE" ]; then
FINAL_COUNT=$(sqlite3 "$DB_FILE" "SELECT COUNT(*) FROM events WHERE kind=1;" 2>/dev/null || echo "0")
FINAL_COUNT=$(psql -d crelay "SELECT COUNT(*) FROM events WHERE kind=1;" 2>/dev/null || echo "0")
echo "Final database stats:"
echo " Total kind 1 events: $FINAL_COUNT"
echo " Database file: $DB_FILE"
+1 -1
View File
@@ -60,4 +60,4 @@ echo "To view logs in real-time:"
echo " tail -f relay.log | grep -E '(partial|write completed|Invalid frame)'"
echo ""
echo "To check if events were stored:"
echo " sqlite3 build/*.db 'SELECT id, length(content) as content_size FROM events ORDER BY created_at DESC LIMIT 4;'"
echo " psql -d crelay 'SELECT id, length(content) as content_size FROM events ORDER BY created_at DESC LIMIT 4;'"
+162
View File
@@ -0,0 +1,162 @@
#!/bin/bash
# Bulk seed the relay with ~1000 test events for the Cleanup page.
# Generates events across multiple kinds, authors, and age ranges.
#
# Usage: ./tests/seed_cleanup_test_events.sh [relay_url]
# Default relay: ws://127.0.0.1:8888
RELAY="${1:-ws://127.0.0.1:8888}"
ADMIN_SK="1111111111111111111111111111111111111111111111111111111111111111"
# Pre-generate 10 random author keys for "non-followed" variety
RANDOM_SKS=()
echo "Generating random author keys..."
for i in $(seq 1 10); do
RANDOM_SKS+=("$(nak key generate 2>/dev/null | head -1)")
done
NOW=$(date +%s)
POSTED=0
FAILED=0
# ── Post a single event ─────────────────────────────────────────────────
post_event() {
local kind="$1"
local content="$2"
local created_at="$3"
local author_sk="$4"
nak event -c "$content" --kind "$kind" --sec "$author_sk" --created-at "$created_at" "$RELAY" >/dev/null 2>&1
if [ $? -eq 0 ]; then
POSTED=$((POSTED + 1))
else
FAILED=$((FAILED + 1))
fi
}
# ── Post N events in a loop ─────────────────────────────────────────────
# Usage: bulk_post <kind> <content_prefix> <count> <age_range_days> [author_sk]
# age_range_days: events will be spread from 0 to N days old
bulk_post() {
local kind="$1"
local prefix="$2"
local count="$3"
local age_range="$4"
local author_sk="${5:-$ADMIN_SK}"
for i in $(seq 1 "$count"); do
local age_seconds=$(( (i * age_range * 86400) / count ))
local ts=$(( NOW - age_seconds ))
post_event "$kind" "${prefix} #${i}" "$ts" "$author_sk"
done
}
# ── Post N events with random author cycling ────────────────────────────
bulk_post_random() {
local kind="$1"
local prefix="$2"
local count="$3"
local age_range="$4"
for i in $(seq 1 "$count"); do
local author_sk="${RANDOM_SKS[$(( (i - 1) % ${#RANDOM_SKS[@]} ))]}"
local age_seconds=$(( (i * age_range * 86400) / count ))
local ts=$(( NOW - age_seconds ))
post_event "$kind" "${prefix} #${i}" "$ts" "$author_sk"
done
}
echo "=== Seeding ~1000 test events to $RELAY ==="
echo ""
# ═══════════════════════════════════════════════════════════════════════
# ADMIN (FOLLOWED) EVENTS
# ═══════════════════════════════════════════════════════════════════════
echo "--- Admin (followed) events ---"
# Kind 1 (text notes) — 100 events spread over 365 days
bulk_post 1 "Admin text note" 100 365
# Kind 7 (reactions) — 50 events spread over 180 days
bulk_post 7 "+" 50 180
# Kind 9734 (zap requests) — 30 events spread over 90 days
bulk_post 9734 "Zap request" 30 90
# Kind 30023 (long-form articles) — 20 events spread over 365 days
bulk_post 30023 "Long form article content for testing the cleanup page with some substantial text to make the events larger and more realistic" 20 365
# Kind 3 (contact lists) — 10 events spread over 365 days
bulk_post 3 '{"contacts":[]}' 10 365
# Kind 0 (profiles) — 5 events spread over 365 days
for i in $(seq 1 5); do
ts=$(( NOW - (i * 365 * 86400 / 5) ))
post_event 0 "{\"name\":\"admin_$i\",\"display_name\":\"Admin User $i\",\"about\":\"Test profile $i\"}" "$ts"
done
echo " → Admin done: $POSTED posted, $FAILED failed"
echo ""
# ═══════════════════════════════════════════════════════════════════════
# RANDOM (NON-FOLLOWED) EVENTS — bulk of the data
# ═══════════════════════════════════════════════════════════════════════
echo "--- Random (non-followed) events ---"
# Kind 1 (text notes) — 400 events spread over 365 days, 10 different authors
bulk_post_random 1 "Random text note" 400 365
# Kind 7 (reactions) — 150 events spread over 180 days
bulk_post_random 7 "+" 150 180
# Kind 9734 (zap requests) — 80 events spread over 90 days
bulk_post_random 9734 "Zap request" 80 90
# Kind 30023 (long-form articles) — 50 events spread over 365 days
bulk_post_random 30023 "Long form article from random user with some substantial content for testing the cleanup page event deletion functionality" 50 365
# Kind 3 (contact lists) — 30 events spread over 365 days
bulk_post_random 3 '{"contacts":[]}' 30 365
# Kind 0 (profiles) — 20 events spread over 365 days
for i in $(seq 1 20); do
author_sk="${RANDOM_SKS[$(( (i - 1) % ${#RANDOM_SKS[@]} ))]}"
ts=$(( NOW - (i * 365 * 86400 / 20) ))
post_event 0 "{\"name\":\"random_$i\",\"display_name\":\"Random User $i\",\"about\":\"Test profile $i\"}" "$ts" "$author_sk"
done
# Kind 6 (reposts) — 30 events spread over 60 days
bulk_post_random 6 "Repost content" 30 60
# Kind 9735 (zap receipt) — 20 events spread over 30 days
bulk_post_random 9735 "Zap receipt" 20 30
echo " → Random done: $POSTED posted, $FAILED failed"
echo ""
# ═══════════════════════════════════════════════════════════════════════
# SUMMARY
# ═══════════════════════════════════════════════════════════════════════
echo "=== Done! ==="
echo " Total posted: $POSTED"
echo " Total failed: $FAILED"
echo ""
echo "Now open http://127.0.0.1:8088/index.php and click 'Cleanup' in the nav."
echo ""
echo "Suggested tests:"
echo " 1. Preview: non-follows, kinds=1, max_age=90"
echo " → should match random kind-1 events older than 90 days"
echo " 2. Preview: non-follows, kinds=1,7, max_age=30"
echo " → should match random kind-1/7 events older than 30 days"
echo " 3. Preview: follows, kinds=1, max_age=90"
echo " → should match admin kind-1 events older than 90 days"
echo " 4. Preview: all, kinds=0"
echo " → should match all kind-0 (profiles)"
echo " 5. Preview: non-follows, kinds=30023, max_age=30"
echo " → should match old random long-form articles"
echo " 6. Save a query, then execute it"
echo " 7. Check the kind breakdown to see size distribution"
+3 -3
View File
@@ -89,7 +89,7 @@ get_subscription_count() {
return
fi
sqlite3 "$db_file" "SELECT COUNT(*) FROM subscriptions WHERE event_type='created' AND ended_at IS NULL;" 2>/dev/null || echo "0"
psql -d crelay "SELECT COUNT(*) FROM subscriptions WHERE event_type='created' AND ended_at IS NULL;" 2>/dev/null || echo "0"
}
# Test 1: Basic Connectivity
@@ -260,14 +260,14 @@ echo "[INFO] Checking database integrity..."
db_file=$(find . -name "*.db" -type f 2>/dev/null | head -1)
if [ -n "$db_file" ]; then
# Check if database is accessible
if sqlite3 "$db_file" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
if psql -d crelay "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
print_result "PASS" "Database integrity check passed"
else
print_result "FAIL" "Database integrity check failed"
fi
# Check subscription table structure
if sqlite3 "$db_file" "SELECT COUNT(*) FROM subscriptions;" &>/dev/null; then
if psql -d crelay "SELECT COUNT(*) FROM subscriptions;" &>/dev/null; then
print_result "PASS" "Subscription table is accessible"
else
print_result "FAIL" "Subscription table is not accessible"