Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c794370a3f | ||
|
|
2ed4b96058 | ||
|
|
c0051b22be | ||
|
|
4cc2d2376e | ||
|
|
30dc4bf67d | ||
|
|
a1928cc5d7 | ||
|
|
7bf0757b1f | ||
|
|
11b0a88cdd | ||
|
|
e8f8e3b0cf | ||
|
|
35b1461ff6 | ||
|
|
87c6aa5e16 | ||
|
|
f0462929ea | ||
|
|
0ccf1959c2 | ||
|
|
ef172c8356 | ||
|
|
ae0370b47f | ||
|
|
137df6253d | ||
|
|
79f06bf1ed | ||
|
|
9e5a42fd3c | ||
|
|
553d398ef5 | ||
|
|
ac0bf17c4f |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,4 +11,3 @@ copy_executable_local.sh
|
||||
nostr_login_lite/
|
||||
style_guide/
|
||||
nostr-tools
|
||||
|
||||
|
||||
995
API.md
Normal file
995
API.md
Normal file
@@ -0,0 +1,995 @@
|
||||
# C-Relay API Documentation
|
||||
|
||||
Complete API reference for the C-Relay event-based administration system and advanced features.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Authentication](#authentication)
|
||||
- [Admin API](#admin-api)
|
||||
- [Event Structure](#event-structure)
|
||||
- [Configuration Management](#configuration-management)
|
||||
- [Auth Rules Management](#auth-rules-management)
|
||||
- [System Commands](#system-commands)
|
||||
- [Database Queries](#database-queries)
|
||||
- [Configuration Reference](#configuration-reference)
|
||||
- [Real-time Monitoring](#real-time-monitoring)
|
||||
- [Direct Message Admin](#direct-message-admin)
|
||||
- [Response Formats](#response-formats)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Examples](#examples)
|
||||
|
||||
## Overview
|
||||
|
||||
C-Relay uses an innovative **event-based administration system** where all configuration and management commands are sent as cryptographically signed Nostr events. This provides:
|
||||
|
||||
- **Cryptographic security**: All commands must be signed with the admin private key
|
||||
- **Audit trail**: Complete history of all administrative actions
|
||||
- **Real-time updates**: Configuration changes applied instantly
|
||||
- **Standard protocol**: Uses Nostr events, no custom protocols
|
||||
- **NIP-44 encryption**: All admin commands and responses are encrypted
|
||||
|
||||
### Key Concepts
|
||||
|
||||
1. **Admin Keypair**: Generated on first startup, used to sign all admin commands
|
||||
2. **Relay Keypair**: The relay's identity on the Nostr network
|
||||
3. **Admin Events**: Kind 23456 events with NIP-44 encrypted commands
|
||||
4. **Response Events**: Kind 23457 events with NIP-44 encrypted responses
|
||||
5. **Event-Based Config**: All settings stored as events in the database
|
||||
|
||||
## Authentication
|
||||
|
||||
### Admin Private Key
|
||||
|
||||
The admin private key is displayed **only once** during first startup:
|
||||
|
||||
```
|
||||
========================================
|
||||
IMPORTANT: SAVE THIS ADMIN PRIVATE KEY
|
||||
========================================
|
||||
Admin Private Key: nsec1abc123...
|
||||
Admin Public Key: npub1def456...
|
||||
========================================
|
||||
```
|
||||
|
||||
**Critical**: Save this key immediately. It cannot be recovered and is required for all administrative operations.
|
||||
|
||||
### Secure Storage
|
||||
|
||||
Store the admin private key securely:
|
||||
|
||||
```bash
|
||||
# Environment variable
|
||||
export C_RELAY_ADMIN_KEY="nsec1abc123..."
|
||||
|
||||
# Secure file
|
||||
echo "nsec1abc123..." > ~/.c-relay-admin
|
||||
chmod 600 ~/.c-relay-admin
|
||||
|
||||
# Password manager (recommended)
|
||||
# Store in 1Password, Bitwarden, etc.
|
||||
```
|
||||
|
||||
### Key Loss Recovery
|
||||
|
||||
If you lose the admin private key:
|
||||
|
||||
1. Stop the relay
|
||||
2. Delete the database file (`*.db`)
|
||||
3. Restart the relay (generates new keys)
|
||||
4. **Note**: This deletes all events and configuration
|
||||
|
||||
## Admin API
|
||||
|
||||
### Event Structure
|
||||
|
||||
All admin commands use the same event structure with NIP-44 encrypted content.
|
||||
|
||||
#### Admin Command Event (Kind 23456)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "computed_event_id",
|
||||
"pubkey": "admin_public_key_hex",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23456,
|
||||
"content": "AqHBUgcM7dXFYLQuDVzGwMST1G8jtWYyVvYxXhVGEu4nAb4LVw...",
|
||||
"tags": [
|
||||
["p", "relay_public_key_hex"]
|
||||
],
|
||||
"sig": "event_signature"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `kind`: Must be `23456` for admin commands
|
||||
- `pubkey`: Admin public key (hex format)
|
||||
- `content`: NIP-44 encrypted JSON array containing the command
|
||||
- `tags`: Must include `["p", "relay_pubkey"]` tag
|
||||
- `sig`: Valid signature from admin private key
|
||||
|
||||
#### Encrypted Content Format
|
||||
|
||||
The `content` field contains a NIP-44 encrypted JSON array:
|
||||
|
||||
```json
|
||||
["command_name", "param1", "param2", ...]
|
||||
```
|
||||
|
||||
Examples:
|
||||
```json
|
||||
["config_query", "all"]
|
||||
["config_update", [{"key": "relay_name", "value": "My Relay", ...}]]
|
||||
["blacklist", "pubkey", "abc123..."]
|
||||
["auth_query", "all"]
|
||||
["sql_query", "SELECT * FROM events LIMIT 10"]
|
||||
```
|
||||
|
||||
#### Admin Response Event (Kind 23457)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key_hex",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "BpKCVhfN8eYtRmPqSvWxZnMkL2gHjUiOp3rTyEwQaS5dFg...",
|
||||
"tags": [
|
||||
["p", "admin_public_key_hex"],
|
||||
["e", "request_event_id"]
|
||||
],
|
||||
"sig": "response_signature"
|
||||
}
|
||||
```
|
||||
|
||||
**Fields**:
|
||||
- `kind`: Always `23457` for admin responses
|
||||
- `pubkey`: Relay public key (hex format)
|
||||
- `content`: NIP-44 encrypted JSON response object
|
||||
- `tags`: Includes `["p", "admin_pubkey"]` and optionally `["e", "request_id"]`
|
||||
- `sig`: Valid signature from relay private key
|
||||
|
||||
### Configuration Management
|
||||
|
||||
#### Query All Configuration
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["config_query", "all"]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "config_all",
|
||||
"total_results": 27,
|
||||
"timestamp": 1234567890,
|
||||
"data": [
|
||||
{
|
||||
"key": "relay_name",
|
||||
"value": "C-Relay",
|
||||
"data_type": "string",
|
||||
"category": "relay",
|
||||
"description": "Relay name displayed in NIP-11"
|
||||
},
|
||||
{
|
||||
"key": "auth_enabled",
|
||||
"value": "false",
|
||||
"data_type": "boolean",
|
||||
"category": "auth",
|
||||
"description": "Enable whitelist/blacklist authentication"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Update Configuration
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["config_update", [
|
||||
{
|
||||
"key": "relay_name",
|
||||
"value": "My Awesome Relay",
|
||||
"data_type": "string",
|
||||
"category": "relay"
|
||||
},
|
||||
{
|
||||
"key": "max_subscriptions_per_client",
|
||||
"value": "50",
|
||||
"data_type": "integer",
|
||||
"category": "limits"
|
||||
}
|
||||
]]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "config_update",
|
||||
"status": "success",
|
||||
"total_results": 2,
|
||||
"timestamp": 1234567890,
|
||||
"data": [
|
||||
{
|
||||
"key": "relay_name",
|
||||
"value": "My Awesome Relay",
|
||||
"status": "updated"
|
||||
},
|
||||
{
|
||||
"key": "max_subscriptions_per_client",
|
||||
"value": "50",
|
||||
"status": "updated"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Auth Rules Management
|
||||
|
||||
#### Add Blacklist Rule
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["blacklist", "pubkey", "abc123def456..."]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "auth_add",
|
||||
"status": "success",
|
||||
"message": "Blacklist rule added successfully",
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
#### Add Whitelist Rule
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["whitelist", "pubkey", "def456abc123..."]
|
||||
```
|
||||
|
||||
#### Delete Auth Rule
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["delete_auth_rule", "blacklist", "pubkey", "abc123def456..."]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "auth_delete",
|
||||
"status": "success",
|
||||
"message": "Auth rule deleted successfully",
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
#### Query All Auth Rules
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["auth_query", "all"]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "auth_rules_all",
|
||||
"total_results": 5,
|
||||
"timestamp": 1234567890,
|
||||
"data": [
|
||||
{
|
||||
"rule_type": "blacklist",
|
||||
"pattern_type": "pubkey",
|
||||
"pattern_value": "abc123...",
|
||||
"action": "deny"
|
||||
},
|
||||
{
|
||||
"rule_type": "whitelist",
|
||||
"pattern_type": "pubkey",
|
||||
"pattern_value": "def456...",
|
||||
"action": "allow"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Query Specific Rule Type
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["auth_query", "whitelist"]
|
||||
```
|
||||
|
||||
#### Query Specific Pattern
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["auth_query", "pattern", "abc123..."]
|
||||
```
|
||||
|
||||
### System Commands
|
||||
|
||||
#### System Status
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["system_command", "system_status"]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "system_status",
|
||||
"timestamp": 1234567890,
|
||||
"status": "running",
|
||||
"uptime_seconds": 86400,
|
||||
"version": "0.6.0",
|
||||
"relay_pubkey": "relay_public_key_hex",
|
||||
"database_size_bytes": 10485760,
|
||||
"total_events": 15432,
|
||||
"active_connections": 42,
|
||||
"active_subscriptions": 156
|
||||
}
|
||||
```
|
||||
|
||||
#### Clear All Auth Rules
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["system_command", "clear_all_auth_rules"]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "system_command",
|
||||
"status": "success",
|
||||
"message": "All auth rules cleared",
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
#### Database Statistics
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["stats_query"]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "stats_query",
|
||||
"timestamp": 1234567890,
|
||||
"database_size_bytes": 10485760,
|
||||
"total_events": 15432,
|
||||
"database_created_at": 1234567800,
|
||||
"latest_event_at": 1234567890,
|
||||
"event_kinds": [
|
||||
{
|
||||
"kind": 1,
|
||||
"count": 12000,
|
||||
"percentage": 77.8
|
||||
},
|
||||
{
|
||||
"kind": 0,
|
||||
"count": 2500,
|
||||
"percentage": 16.2
|
||||
}
|
||||
],
|
||||
"time_stats": {
|
||||
"total": 15432,
|
||||
"last_24h": 234,
|
||||
"last_7d": 1456,
|
||||
"last_30d": 5432
|
||||
},
|
||||
"top_pubkeys": [
|
||||
{
|
||||
"pubkey": "abc123...",
|
||||
"event_count": 1234,
|
||||
"percentage": 8.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Database Queries
|
||||
|
||||
#### SQL Query Command
|
||||
|
||||
Execute read-only SQL queries against the relay database.
|
||||
|
||||
**Command**:
|
||||
```json
|
||||
["sql_query", "SELECT * FROM events ORDER BY created_at DESC LIMIT 10"]
|
||||
```
|
||||
|
||||
**Response** (decrypted):
|
||||
```json
|
||||
{
|
||||
"query_type": "sql_query",
|
||||
"request_id": "request_event_id",
|
||||
"timestamp": 1234567890,
|
||||
"query": "SELECT * FROM events ORDER BY created_at DESC LIMIT 10",
|
||||
"execution_time_ms": 45,
|
||||
"row_count": 10,
|
||||
"columns": ["id", "pubkey", "created_at", "kind", "content", "tags", "sig"],
|
||||
"rows": [
|
||||
["abc123...", "def456...", 1234567890, 1, "Hello world", "[]", "sig123..."],
|
||||
["ghi789...", "jkl012...", 1234567880, 0, "{\"name\":\"Alice\"}", "[]", "sig456..."]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Security Features
|
||||
|
||||
- **Read-only**: Only SELECT statements allowed
|
||||
- **Query timeout**: 5 seconds maximum
|
||||
- **Result limit**: 1000 rows maximum
|
||||
- **Logging**: All queries logged with execution time
|
||||
- **Validation**: SQL injection protection
|
||||
|
||||
#### Available Tables and Views
|
||||
|
||||
**Core Tables**:
|
||||
- `events` - All Nostr events
|
||||
- `config` - Configuration parameters
|
||||
- `auth_rules` - Authentication rules
|
||||
- `subscription_events` - Subscription lifecycle log
|
||||
- `event_broadcasts` - Event broadcast log
|
||||
|
||||
**Views**:
|
||||
- `recent_events` - Last 1000 events
|
||||
- `event_stats` - Event statistics by type
|
||||
- `subscription_analytics` - Subscription metrics
|
||||
- `active_subscriptions_log` - Currently active subscriptions
|
||||
- `event_kinds_view` - Event distribution by kind
|
||||
- `top_pubkeys_view` - Top 10 pubkeys by event count
|
||||
- `time_stats_view` - Time-based statistics
|
||||
|
||||
#### Example Queries
|
||||
|
||||
**Recent events**:
|
||||
```sql
|
||||
SELECT id, pubkey, created_at, kind
|
||||
FROM events
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20
|
||||
```
|
||||
|
||||
**Event distribution**:
|
||||
```sql
|
||||
SELECT * FROM event_kinds_view
|
||||
ORDER BY count DESC
|
||||
```
|
||||
|
||||
**Active subscriptions**:
|
||||
```sql
|
||||
SELECT * FROM active_subscriptions_log
|
||||
ORDER BY created_at DESC
|
||||
```
|
||||
|
||||
**Database statistics**:
|
||||
```sql
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM events) as total_events,
|
||||
(SELECT COUNT(*) FROM subscription_events) as total_subscriptions,
|
||||
(SELECT COUNT(DISTINCT pubkey) FROM events) as unique_pubkeys
|
||||
```
|
||||
|
||||
**Events by specific pubkey**:
|
||||
```sql
|
||||
SELECT id, created_at, kind, content
|
||||
FROM events
|
||||
WHERE pubkey = 'abc123...'
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 50
|
||||
```
|
||||
|
||||
**Events in time range**:
|
||||
```sql
|
||||
SELECT COUNT(*) as count, kind
|
||||
FROM events
|
||||
WHERE created_at BETWEEN 1234567000 AND 1234567890
|
||||
GROUP BY kind
|
||||
ORDER BY count DESC
|
||||
```
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
### Basic Relay Settings
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `relay_name` | string | "C-Relay" | Relay name (NIP-11) |
|
||||
| `relay_description` | string | "C Nostr Relay" | Relay description |
|
||||
| `relay_contact` | string | "" | Admin contact info |
|
||||
| `relay_software` | string | "c-relay" | Software identifier |
|
||||
| `relay_version` | string | auto | Software version |
|
||||
| `supported_nips` | string | "1,9,11,13,15,20,33,40,42,45,50,70" | Supported NIPs |
|
||||
| `language_tags` | string | "*" | Supported languages |
|
||||
| `relay_countries` | string | "*" | Supported countries |
|
||||
| `posting_policy` | string | "" | Posting policy URL |
|
||||
| `payments_url` | string | "" | Payment URL |
|
||||
|
||||
### Connection & Limits
|
||||
|
||||
| Key | Type | Default | Range | Restart Required |
|
||||
|-----|------|---------|-------|------------------|
|
||||
| `max_connections` | integer | 1000 | 1-10000 | Yes |
|
||||
| `max_subscriptions_per_client` | integer | 25 | 1-100 | No |
|
||||
| `max_total_subscriptions` | integer | 5000 | 100-50000 | No |
|
||||
| `max_message_length` | integer | 65536 | 1024-1048576 | No |
|
||||
| `max_event_tags` | integer | 2000 | 10-10000 | No |
|
||||
| `max_content_length` | integer | 65536 | 1-1048576 | No |
|
||||
|
||||
### Authentication & Access Control
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `auth_enabled` | boolean | false | Enable whitelist/blacklist |
|
||||
| `nip42_auth_required` | boolean | false | Require NIP-42 auth |
|
||||
| `nip42_auth_required_kinds` | string | "" | Kinds requiring NIP-42 |
|
||||
| `nip42_challenge_timeout` | integer | 300 | Challenge timeout (seconds) |
|
||||
|
||||
### Proof of Work (NIP-13)
|
||||
|
||||
| Key | Type | Default | Values | Description |
|
||||
|-----|------|---------|--------|-------------|
|
||||
| `pow_min_difficulty` | integer | 0 | 0-40 | Minimum PoW difficulty |
|
||||
| `pow_mode` | string | "optional" | disabled/optional/required | PoW enforcement mode |
|
||||
|
||||
### Event Expiration (NIP-40)
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `nip40_expiration_enabled` | boolean | true | Enable expiration support |
|
||||
| `nip40_expiration_strict` | boolean | false | Reject expired events |
|
||||
| `nip40_expiration_filter` | boolean | true | Filter expired from results |
|
||||
| `nip40_expiration_grace_period` | integer | 300 | Grace period (seconds) |
|
||||
|
||||
### Monitoring
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `kind_24567_reporting_throttle_sec` | integer | 5 | Monitoring event throttle |
|
||||
|
||||
### Dynamic vs Restart-Required
|
||||
|
||||
**Dynamic (No Restart)**:
|
||||
- All NIP-11 relay information
|
||||
- Authentication settings
|
||||
- Subscription limits
|
||||
- Event validation limits
|
||||
- Proof of Work settings
|
||||
- Expiration settings
|
||||
|
||||
**Restart Required**:
|
||||
- `max_connections`
|
||||
- `relay_port`
|
||||
- Database settings
|
||||
|
||||
## Real-time Monitoring
|
||||
|
||||
C-Relay provides subscription-based real-time monitoring using ephemeral events (kind 24567).
|
||||
|
||||
### Activation
|
||||
|
||||
Subscribe to kind 24567 events to activate monitoring:
|
||||
|
||||
```json
|
||||
["REQ", "monitoring-sub", {"kinds": [24567]}]
|
||||
```
|
||||
|
||||
### Monitoring Event Types
|
||||
|
||||
Subscribe to specific monitoring types using d-tag filters:
|
||||
|
||||
```json
|
||||
["REQ", "event-kinds", {"kinds": [24567], "#d": ["event_kinds"]}]
|
||||
["REQ", "time-stats", {"kinds": [24567], "#d": ["time_stats"]}]
|
||||
["REQ", "top-pubkeys", {"kinds": [24567], "#d": ["top_pubkeys"]}]
|
||||
["REQ", "cpu-metrics", {"kinds": [24567], "#d": ["cpu_metrics"]}]
|
||||
```
|
||||
|
||||
### Event Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 24567,
|
||||
"pubkey": "relay_pubkey",
|
||||
"created_at": 1234567890,
|
||||
"content": "{\"data_type\":\"event_kinds\",\"timestamp\":1234567890,...}",
|
||||
"tags": [
|
||||
["d", "event_kinds"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Monitoring Types
|
||||
|
||||
#### Event Distribution (`event_kinds`)
|
||||
|
||||
```json
|
||||
{
|
||||
"data_type": "event_kinds",
|
||||
"timestamp": 1234567890,
|
||||
"total_events": 15432,
|
||||
"kinds": [
|
||||
{"kind": 1, "count": 12000, "percentage": 77.8},
|
||||
{"kind": 0, "count": 2500, "percentage": 16.2}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Time Statistics (`time_stats`)
|
||||
|
||||
```json
|
||||
{
|
||||
"data_type": "time_stats",
|
||||
"timestamp": 1234567890,
|
||||
"total_events": 15432,
|
||||
"last_24h": 234,
|
||||
"last_7d": 1456,
|
||||
"last_30d": 5432
|
||||
}
|
||||
```
|
||||
|
||||
#### Top Publishers (`top_pubkeys`)
|
||||
|
||||
```json
|
||||
{
|
||||
"data_type": "top_pubkeys",
|
||||
"timestamp": 1234567890,
|
||||
"top_pubkeys": [
|
||||
{"pubkey": "abc123...", "count": 1234, "percentage": 8.0},
|
||||
{"pubkey": "def456...", "count": 987, "percentage": 6.4}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### CPU Metrics (`cpu_metrics`)
|
||||
|
||||
```json
|
||||
{
|
||||
"data_type": "cpu_metrics",
|
||||
"timestamp": 1234567890,
|
||||
"cpu_percent": 12.5,
|
||||
"memory_mb": 45.2,
|
||||
"uptime_seconds": 86400
|
||||
}
|
||||
```
|
||||
|
||||
#### Active Subscriptions (`active_subscriptions`) - Admin Only
|
||||
|
||||
```json
|
||||
{
|
||||
"data_type": "active_subscriptions",
|
||||
"timestamp": 1234567890,
|
||||
"total_subscriptions": 156,
|
||||
"subscriptions_by_client": [
|
||||
{"client_id": "client1", "count": 12},
|
||||
{"client_id": "client2", "count": 8}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
Control monitoring frequency:
|
||||
|
||||
```json
|
||||
["config_update", [{
|
||||
"key": "kind_24567_reporting_throttle_sec",
|
||||
"value": "10",
|
||||
"data_type": "integer",
|
||||
"category": "monitoring"
|
||||
}]]
|
||||
```
|
||||
|
||||
### Performance
|
||||
|
||||
- Events are ephemeral (not stored)
|
||||
- Automatic activation/deactivation based on subscriptions
|
||||
- Throttling prevents excessive event generation
|
||||
- Minimal overhead when no clients monitoring
|
||||
|
||||
## Direct Message Admin
|
||||
|
||||
Control your relay by sending direct messages from any Nostr client.
|
||||
|
||||
### Setup
|
||||
|
||||
1. The relay has its own keypair (shown on startup)
|
||||
2. The relay knows the admin public key
|
||||
3. Send NIP-17 direct messages to the relay
|
||||
|
||||
### Available Commands
|
||||
|
||||
Send a DM containing any of these keywords:
|
||||
|
||||
| Command | Aliases | Response |
|
||||
|---------|---------|----------|
|
||||
| Statistics | stats, statistics | Database statistics |
|
||||
| Configuration | config, configuration | Current configuration |
|
||||
|
||||
### Example
|
||||
|
||||
Using any Nostr client that supports NIP-17:
|
||||
|
||||
1. Find the relay's public key (shown on startup)
|
||||
2. Send a DM: "stats"
|
||||
3. Receive a DM with current relay statistics
|
||||
|
||||
### Response Format
|
||||
|
||||
The relay responds with a NIP-17 DM containing:
|
||||
|
||||
**Stats Response**:
|
||||
```
|
||||
Relay Statistics
|
||||
================
|
||||
Total Events: 15,432
|
||||
Database Size: 10.5 MB
|
||||
Active Connections: 42
|
||||
Active Subscriptions: 156
|
||||
Uptime: 1 day, 2 hours
|
||||
```
|
||||
|
||||
**Config Response**:
|
||||
```
|
||||
Relay Configuration
|
||||
===================
|
||||
Name: My Awesome Relay
|
||||
Description: Community relay
|
||||
Max Subscriptions: 25
|
||||
Auth Enabled: false
|
||||
PoW Difficulty: 0
|
||||
```
|
||||
|
||||
## Response Formats
|
||||
|
||||
### Success Response
|
||||
|
||||
```json
|
||||
{
|
||||
"query_type": "command_name",
|
||||
"status": "success",
|
||||
"message": "Operation completed successfully",
|
||||
"timestamp": 1234567890,
|
||||
"data": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### Error Response
|
||||
|
||||
```json
|
||||
{
|
||||
"query_type": "command_name",
|
||||
"status": "error",
|
||||
"error": "Error description",
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
### Query Response
|
||||
|
||||
```json
|
||||
{
|
||||
"query_type": "query_name",
|
||||
"total_results": 10,
|
||||
"timestamp": 1234567890,
|
||||
"data": [...]
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Common Errors
|
||||
|
||||
| Error | Cause | Solution |
|
||||
|-------|-------|----------|
|
||||
| `invalid_signature` | Event signature invalid | Check admin private key |
|
||||
| `unauthorized` | Wrong admin pubkey | Use correct admin key |
|
||||
| `invalid_command` | Unknown command | Check command format |
|
||||
| `validation_failed` | Invalid parameter value | Check parameter ranges |
|
||||
| `database_error` | Database operation failed | Check database integrity |
|
||||
| `timeout` | Query took too long | Simplify query or increase timeout |
|
||||
|
||||
### Error Response Example
|
||||
|
||||
```json
|
||||
{
|
||||
"query_type": "config_update",
|
||||
"status": "error",
|
||||
"error": "field validation failed: invalid port number '99999' (must be 1-65535)",
|
||||
"timestamp": 1234567890
|
||||
}
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### JavaScript/TypeScript Example
|
||||
|
||||
```javascript
|
||||
import { SimplePool, nip44, getPublicKey, finalizeEvent } from 'nostr-tools';
|
||||
|
||||
const adminPrivkey = 'your_admin_privkey_hex';
|
||||
const adminPubkey = getPublicKey(adminPrivkey);
|
||||
const relayPubkey = 'relay_pubkey_hex';
|
||||
const relayUrl = 'ws://localhost:8888';
|
||||
|
||||
// Create admin command
|
||||
async function sendAdminCommand(command) {
|
||||
const pool = new SimplePool();
|
||||
|
||||
// Encrypt command with NIP-44
|
||||
const encryptedContent = await nip44.encrypt(
|
||||
adminPrivkey,
|
||||
relayPubkey,
|
||||
JSON.stringify(command)
|
||||
);
|
||||
|
||||
// Create event
|
||||
const event = finalizeEvent({
|
||||
kind: 23456,
|
||||
created_at: Math.floor(Date.now() / 1000),
|
||||
tags: [['p', relayPubkey]],
|
||||
content: encryptedContent
|
||||
}, adminPrivkey);
|
||||
|
||||
// Publish event
|
||||
await pool.publish([relayUrl], event);
|
||||
|
||||
// Subscribe to response
|
||||
const sub = pool.sub([relayUrl], [{
|
||||
kinds: [23457],
|
||||
'#p': [adminPubkey],
|
||||
since: Math.floor(Date.now() / 1000)
|
||||
}]);
|
||||
|
||||
return new Promise((resolve) => {
|
||||
sub.on('event', async (event) => {
|
||||
// Decrypt response
|
||||
const decrypted = await nip44.decrypt(
|
||||
adminPrivkey,
|
||||
relayPubkey,
|
||||
event.content
|
||||
);
|
||||
resolve(JSON.parse(decrypted));
|
||||
sub.unsub();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Query configuration
|
||||
const config = await sendAdminCommand(['config_query', 'all']);
|
||||
console.log(config);
|
||||
|
||||
// Update configuration
|
||||
const result = await sendAdminCommand(['config_update', [
|
||||
{
|
||||
key: 'relay_name',
|
||||
value: 'My Relay',
|
||||
data_type: 'string',
|
||||
category: 'relay'
|
||||
}
|
||||
]]);
|
||||
console.log(result);
|
||||
|
||||
// Add blacklist rule
|
||||
const blacklist = await sendAdminCommand([
|
||||
'blacklist',
|
||||
'pubkey',
|
||||
'abc123...'
|
||||
]);
|
||||
console.log(blacklist);
|
||||
|
||||
// Execute SQL query
|
||||
const query = await sendAdminCommand([
|
||||
'sql_query',
|
||||
'SELECT * FROM events ORDER BY created_at DESC LIMIT 10'
|
||||
]);
|
||||
console.log(query);
|
||||
```
|
||||
|
||||
### Python Example
|
||||
|
||||
```python
|
||||
from nostr_sdk import Keys, Client, EventBuilder, Filter, nip44
|
||||
|
||||
admin_privkey = "your_admin_privkey_hex"
|
||||
relay_pubkey = "relay_pubkey_hex"
|
||||
relay_url = "ws://localhost:8888"
|
||||
|
||||
# Initialize
|
||||
keys = Keys.parse(admin_privkey)
|
||||
client = Client(keys)
|
||||
client.add_relay(relay_url)
|
||||
client.connect()
|
||||
|
||||
# Send admin command
|
||||
async def send_admin_command(command):
|
||||
# Encrypt command
|
||||
encrypted = nip44.encrypt(
|
||||
keys.secret_key(),
|
||||
relay_pubkey,
|
||||
json.dumps(command)
|
||||
)
|
||||
|
||||
# Create event
|
||||
event = EventBuilder.new(
|
||||
kind=23456,
|
||||
content=encrypted,
|
||||
tags=[["p", relay_pubkey]]
|
||||
).to_event(keys)
|
||||
|
||||
# Publish
|
||||
await client.send_event(event)
|
||||
|
||||
# Wait for response
|
||||
filter = Filter().kind(23457).pubkey(relay_pubkey).since(int(time.time()))
|
||||
events = await client.get_events_of([filter], timeout=5)
|
||||
|
||||
if events:
|
||||
# Decrypt response
|
||||
decrypted = nip44.decrypt(
|
||||
keys.secret_key(),
|
||||
relay_pubkey,
|
||||
events[0].content()
|
||||
)
|
||||
return json.loads(decrypted)
|
||||
|
||||
# Query configuration
|
||||
config = await send_admin_command(["config_query", "all"])
|
||||
print(config)
|
||||
```
|
||||
|
||||
### Bash/curl Example
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Note: This is a simplified example. Real implementation requires:
|
||||
# - NIP-44 encryption
|
||||
# - Event signing
|
||||
# - WebSocket connection
|
||||
|
||||
RELAY_URL="ws://localhost:8888"
|
||||
ADMIN_PRIVKEY="your_admin_privkey"
|
||||
RELAY_PUBKEY="relay_pubkey"
|
||||
|
||||
# Use nostrtool or similar for proper event creation
|
||||
nostrtool event \
|
||||
--kind 23456 \
|
||||
--content "$(echo '["config_query","all"]' | nip44-encrypt)" \
|
||||
--tag p "$RELAY_PUBKEY" \
|
||||
--private-key "$ADMIN_PRIVKEY" \
|
||||
| nostrtool send "$RELAY_URL"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **[Configuration Guide](docs/configuration_guide.md)** - Detailed configuration options
|
||||
- **[Deployment Guide](docs/deployment_guide.md)** - Production deployment
|
||||
- **[NIP-42 Authentication](docs/NIP-42_Authentication.md)** - Authentication setup
|
||||
- **[User Guide](docs/user_guide.md)** - End-user documentation
|
||||
|
||||
## Support
|
||||
|
||||
For API questions or issues:
|
||||
- Open an issue on GitHub
|
||||
- Check existing documentation
|
||||
- Join the Nostr community
|
||||
|
||||
---
|
||||
|
||||
**API Version**: 0.6.0
|
||||
**Last Updated**: 2026-01-23
|
||||
@@ -107,13 +107,13 @@ COPY Makefile /build/Makefile
|
||||
# Disable fortification to avoid __*_chk symbols that don't exist in MUSL
|
||||
# Use conditional compilation flags based on DEBUG_BUILD argument
|
||||
RUN if [ "$DEBUG_BUILD" = "true" ]; then \
|
||||
CFLAGS="-g -O0 -DDEBUG"; \
|
||||
STRIP_CMD=""; \
|
||||
echo "Building with DEBUG symbols enabled"; \
|
||||
CFLAGS="-g -O2 -DDEBUG"; \
|
||||
STRIP_CMD="echo 'Keeping debug symbols'"; \
|
||||
echo "Building with DEBUG symbols enabled (optimized with -O2)"; \
|
||||
else \
|
||||
CFLAGS="-O2"; \
|
||||
STRIP_CMD="strip /build/c_relay_static"; \
|
||||
echo "Building optimized production binary"; \
|
||||
echo "Building optimized production binary (symbols stripped)"; \
|
||||
fi && \
|
||||
gcc -static $CFLAGS -Wall -Wextra -std=c99 \
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
|
||||
|
||||
619
README.md
619
README.md
@@ -1,474 +1,285 @@
|
||||
# C-Nostr Relay
|
||||
# C-Relay: High-Performance Nostr Relay
|
||||
|
||||
A high-performance Nostr relay implemented in C with SQLite backend, featuring nostr event-based management.
|
||||
A blazingly fast, production-ready Nostr relay implemented in C with an innovative event-based configuration system. Built for performance, security, and ease of deployment.
|
||||
|
||||
## Supported NIPs
|
||||
## 🚀 Why C-Relay?
|
||||
|
||||
<!--
|
||||
NOTE FOR ASSISTANTS: When updating the NIPs checklist below, ONLY change [ ] to [x] to mark as complete.
|
||||
Do NOT modify the formatting, add emojis, or change the text. Keep the simple format consistent.
|
||||
-->
|
||||
### Event-Based Configuration
|
||||
Unlike traditional relays that require config files, C-Relay uses **cryptographically signed Nostr events** for all configuration. This means:
|
||||
- **Zero config files** - Everything stored in the database
|
||||
- **Real-time updates** - Changes applied instantly without restart
|
||||
- **Cryptographic security** - All changes must be signed by admin
|
||||
- **Complete audit trail** - Every configuration change is timestamped and signed
|
||||
- **Version control built-in** - Configuration history is part of the event stream
|
||||
|
||||
- [x] NIP-01: Basic protocol flow implementation
|
||||
- [x] NIP-09: Event deletion
|
||||
- [x] NIP-11: Relay information document
|
||||
- [x] NIP-13: Proof of Work
|
||||
- [x] NIP-15: End of Stored Events Notice
|
||||
- [x] NIP-20: Command Results
|
||||
- [x] NIP-33: Parameterized Replaceable Events
|
||||
- [x] NIP-40: Expiration Timestamp
|
||||
- [x] NIP-42: Authentication of clients to relays
|
||||
- [x] NIP-45: Counting results
|
||||
- [x] NIP-50: Keywords filter
|
||||
- [x] NIP-70: Protected Events
|
||||
### Built-in Web Admin Interface
|
||||
Access a full-featured web dashboard at `http://localhost:8888/api/` with:
|
||||
- Real-time configuration management
|
||||
- Database statistics and analytics
|
||||
- Auth rules management (whitelist/blacklist)
|
||||
- NIP-42 authentication for secure access
|
||||
- No external dependencies - all files embedded in the binary
|
||||
|
||||
## Quick Start
|
||||
### Direct Message Admin System
|
||||
Control your relay by sending direct messages from any Nostr client:
|
||||
- Send "stats" to get relay statistics
|
||||
- Send "config" to view current configuration
|
||||
- Full Nostr citizen with its own keypair
|
||||
- Works with any NIP-17 compatible client
|
||||
|
||||
Get your C-Relay up and running in minutes with a static binary (no dependencies required):
|
||||
### Performance & Efficiency
|
||||
- **Written in C** - Maximum performance and minimal resource usage
|
||||
- **SQLite backend** - Reliable, fast, and self-contained
|
||||
- **Static binary available** - Single file deployment with zero dependencies
|
||||
- **Efficient memory management** - Optimized for long-running operation
|
||||
- **WebSocket native** - Built on libwebsockets for optimal protocol support
|
||||
|
||||
### 1. Download Static Binary
|
||||
## 📋 Supported NIPs
|
||||
|
||||
Download the latest static release from the [releases page](https://git.laantungir.net/laantungir/c-relay/releases):
|
||||
C-Relay implements a comprehensive set of Nostr Improvement Proposals:
|
||||
|
||||
- ✅ **NIP-01**: Basic protocol flow implementation
|
||||
- ✅ **NIP-09**: Event deletion
|
||||
- ✅ **NIP-11**: Relay information document
|
||||
- ✅ **NIP-13**: Proof of Work
|
||||
- ✅ **NIP-15**: End of Stored Events Notice
|
||||
- ✅ **NIP-20**: Command Results
|
||||
- ✅ **NIP-33**: Parameterized Replaceable Events
|
||||
- ✅ **NIP-40**: Expiration Timestamp
|
||||
- ✅ **NIP-42**: Authentication of clients to relays
|
||||
- ✅ **NIP-45**: Counting results
|
||||
- ✅ **NIP-50**: Keywords filter
|
||||
- ✅ **NIP-70**: Protected Events
|
||||
|
||||
## 🎯 Key Features
|
||||
|
||||
### Security
|
||||
- **NIP-42 Authentication** - Cryptographic client authentication
|
||||
- **Proof of Work** - Configurable PoW requirements (NIP-13)
|
||||
- **Protected Events** - Support for encrypted/protected content (NIP-70)
|
||||
- **Whitelist/Blacklist** - Flexible access control by pubkey
|
||||
- **Admin key security** - Private key shown only once, never stored
|
||||
|
||||
### Flexibility
|
||||
- **Dynamic configuration** - Most settings update without restart
|
||||
- **Subscription management** - Configurable limits per client and globally
|
||||
- **Event expiration** - Automatic cleanup of expired events (NIP-40)
|
||||
- **Parameterized events** - Full support for replaceable events (NIP-33)
|
||||
- **Keyword search** - Built-in full-text search (NIP-50)
|
||||
|
||||
### Monitoring
|
||||
- **Real-time statistics** - Live event distribution and metrics
|
||||
- **Subscription-based monitoring** - Ephemeral events (kind 24567) for dashboards
|
||||
- **SQL query API** - Direct database queries for advanced analytics
|
||||
- **Resource tracking** - CPU, memory, and database size monitoring
|
||||
- **Event broadcast logging** - Complete audit trail of all operations
|
||||
|
||||
### Developer-Friendly
|
||||
- **Comprehensive test suite** - Automated tests for all NIPs
|
||||
- **Clear documentation** - Detailed guides for deployment and configuration
|
||||
- **SystemD integration** - Production-ready service files included
|
||||
- **Docker support** - Container deployment with Alpine Linux
|
||||
- **Cross-platform** - Builds on Linux, macOS, and Windows (WSL)
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Option 1: Static Binary (Recommended)
|
||||
|
||||
Download and run - no dependencies required:
|
||||
|
||||
```bash
|
||||
# Static binary - works on all Linux distributions (no dependencies)
|
||||
# Download the latest static release
|
||||
wget https://git.laantungir.net/laantungir/c-relay/releases/download/v0.6.0/c-relay-v0.6.0-linux-x86_64-static
|
||||
chmod +x c-relay-v0.6.0-linux-x86_64-static
|
||||
mv c-relay-v0.6.0-linux-x86_64-static c-relay
|
||||
```
|
||||
|
||||
### 2. Start the Relay
|
||||
|
||||
Simply run the binary - no configuration files needed:
|
||||
|
||||
```bash
|
||||
# Run the relay
|
||||
./c-relay
|
||||
```
|
||||
|
||||
On first startup, you'll see:
|
||||
- **Admin Private Key**: Save this securely! You'll need it for administration
|
||||
- **Relay Public Key**: Your relay's identity on the Nostr network
|
||||
- **Port Information**: Default is 8888, or the next available port
|
||||
**Important**: On first startup, save the **Admin Private Key** displayed in the console. You'll need it for all administrative operations.
|
||||
|
||||
### 3. Access the Web Interface
|
||||
### Option 2: Build from Source
|
||||
|
||||
```bash
|
||||
# Install dependencies (Ubuntu/Debian)
|
||||
sudo apt install -y build-essential git sqlite3 libsqlite3-dev \
|
||||
libwebsockets-dev libssl-dev libsecp256k1-dev libcurl4-openssl-dev zlib1g-dev
|
||||
|
||||
# Clone and build
|
||||
git clone https://github.com/your-org/c-relay.git
|
||||
cd c-relay
|
||||
git submodule update --init --recursive
|
||||
./make_and_restart_relay.sh
|
||||
```
|
||||
|
||||
The relay will start on port 8888 (or the next available port).
|
||||
|
||||
## 🌐 Access the Web Interface
|
||||
|
||||
Once running, open your browser to:
|
||||
|
||||
Open your browser and navigate to:
|
||||
```
|
||||
http://localhost:8888/api/
|
||||
```
|
||||
|
||||
The web interface provides:
|
||||
- Real-time configuration management
|
||||
- Database statistics dashboard
|
||||
- Auth rules management
|
||||
- Secure admin authentication with your Nostr identity
|
||||
- Configuration management with live updates
|
||||
- Database statistics and event distribution charts
|
||||
- Auth rules management (whitelist/blacklist)
|
||||
- SQL query interface for advanced analytics
|
||||
- Real-time monitoring dashboard
|
||||
|
||||
### 4. Test Your Relay
|
||||
## 📦 Installation Options
|
||||
|
||||
### Production Deployment (SystemD)
|
||||
|
||||
Test basic connectivity:
|
||||
```bash
|
||||
# Test WebSocket connection
|
||||
curl -H "Accept: application/nostr+json" http://localhost:8888
|
||||
# Clone repository
|
||||
git clone https://github.com/your-org/c-relay.git
|
||||
cd c-relay
|
||||
git submodule update --init --recursive
|
||||
|
||||
# Test with a Nostr client
|
||||
# Add ws://localhost:8888 to your client's relay list
|
||||
# Build
|
||||
make clean && make
|
||||
|
||||
# Install as system service
|
||||
sudo systemd/install-service.sh
|
||||
|
||||
# Start and enable
|
||||
sudo systemctl start c-relay
|
||||
sudo systemctl enable c-relay
|
||||
|
||||
# Capture admin keys from logs
|
||||
sudo journalctl -u c-relay | grep "Admin Private Key"
|
||||
```
|
||||
|
||||
### 5. Configure Your Relay (Optional)
|
||||
### Docker Deployment
|
||||
|
||||
Use the web interface or send admin commands to customize:
|
||||
- Relay name and description
|
||||
- Authentication rules (whitelist/blacklist)
|
||||
- Connection limits
|
||||
- Proof-of-work requirements
|
||||
```bash
|
||||
# Build Docker image
|
||||
docker build -f Dockerfile.alpine-musl -t c-relay .
|
||||
|
||||
**That's it!** Your relay is now running with zero configuration required. The event-based configuration system means you can adjust all settings through the web interface or admin API without editing config files.
|
||||
|
||||
|
||||
## Web Admin Interface
|
||||
|
||||
C-Relay includes a **built-in web-based administration interface** accessible at `http://localhost:8888/api/`. The interface provides:
|
||||
|
||||
- **Real-time Configuration Management**: View and edit all relay settings through a web UI
|
||||
- **Database Statistics Dashboard**: Monitor event counts, storage usage, and performance metrics
|
||||
- **Auth Rules Management**: Configure whitelist/blacklist rules for pubkeys
|
||||
- **NIP-42 Authentication**: Secure access using your Nostr identity
|
||||
- **Event-Based Updates**: All changes are applied as cryptographically signed Nostr events
|
||||
|
||||
The web interface serves embedded static files with no external dependencies and includes proper CORS headers for browser compatibility.
|
||||
|
||||
|
||||
## Administrator API
|
||||
|
||||
C-Relay uses an innovative **event-based administration system** where all configuration and management commands are sent as signed Nostr events using the admin private key generated during first startup. All admin commands use **NIP-44 encrypted command arrays** for security and compatibility.
|
||||
|
||||
### Authentication
|
||||
|
||||
All admin commands require signing with the admin private key displayed during first-time startup. **Save this key securely** - it cannot be recovered and is needed for all administrative operations.
|
||||
|
||||
### Event Structure
|
||||
|
||||
All admin commands use the same unified event structure with NIP-44 encrypted content:
|
||||
|
||||
**Admin Command Event:**
|
||||
```json
|
||||
{
|
||||
"id": "event_id",
|
||||
"pubkey": "admin_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23456,
|
||||
"content": "AqHBUgcM7dXFYLQuDVzGwMST1G8jtWYyVvYxXhVGEu4nAb4LVw...",
|
||||
"tags": [
|
||||
["p", "relay_public_key"]
|
||||
],
|
||||
"sig": "event_signature"
|
||||
}
|
||||
# Run container
|
||||
docker run -d \
|
||||
--name c-relay \
|
||||
-p 8888:8888 \
|
||||
-v /path/to/data:/data \
|
||||
c-relay
|
||||
```
|
||||
|
||||
The `content` field contains a NIP-44 encrypted JSON array representing the command.
|
||||
### Cloud Deployment
|
||||
|
||||
**Admin Response Event:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "BpKCVhfN8eYtRmPqSvWxZnMkL2gHjUiOp3rTyEwQaS5dFg...",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
Quick deployment scripts for popular cloud providers:
|
||||
|
||||
```bash
|
||||
# AWS, GCP, DigitalOcean, etc.
|
||||
sudo examples/deployment/simple-vps/deploy.sh
|
||||
```
|
||||
|
||||
The `content` field contains a NIP-44 encrypted JSON response object.
|
||||
See [`docs/deployment_guide.md`](docs/deployment_guide.md) for detailed deployment instructions.
|
||||
|
||||
### Admin Commands
|
||||
## 🔧 Configuration
|
||||
|
||||
All commands are sent as NIP-44 encrypted JSON arrays in the event content. The following table lists all available commands:
|
||||
C-Relay uses an innovative event-based configuration system. All settings are managed through signed Nostr events.
|
||||
|
||||
| Command Type | Command Format | Description |
|
||||
|--------------|----------------|-------------|
|
||||
| **Configuration Management** |
|
||||
| `config_update` | `["config_update", [{"key": "auth_enabled", "value": "true", "data_type": "boolean", "category": "auth"}, {"key": "relay_description", "value": "My Relay", "data_type": "string", "category": "relay"}, ...]]` | Update relay configuration parameters (supports multiple updates) |
|
||||
| `config_query` | `["config_query", "all"]` | Query all configuration parameters |
|
||||
| **Auth Rules Management** |
|
||||
| `auth_add_blacklist` | `["blacklist", "pubkey", "abc123..."]` | Add pubkey to blacklist |
|
||||
| `auth_add_whitelist` | `["whitelist", "pubkey", "def456..."]` | Add pubkey to whitelist |
|
||||
| `auth_delete_rule` | `["delete_auth_rule", "blacklist", "pubkey", "abc123..."]` | Delete specific auth rule |
|
||||
| `auth_query_all` | `["auth_query", "all"]` | Query all auth rules |
|
||||
| `auth_query_type` | `["auth_query", "whitelist"]` | Query specific rule type |
|
||||
| `auth_query_pattern` | `["auth_query", "pattern", "abc123..."]` | Query specific pattern |
|
||||
| **System Commands** |
|
||||
| `system_clear_auth` | `["system_command", "clear_all_auth_rules"]` | Clear all auth rules |
|
||||
| `system_status` | `["system_command", "system_status"]` | Get system status |
|
||||
| `stats_query` | `["stats_query"]` | Get comprehensive database statistics |
|
||||
| **Database Queries** |
|
||||
| `sql_query` | `["sql_query", "SELECT * FROM events LIMIT 10"]` | Execute read-only SQL query against relay database |
|
||||
### Basic Configuration
|
||||
|
||||
### Available Configuration Keys
|
||||
Use the web interface at `http://localhost:8888/api/` or send admin commands via the API.
|
||||
|
||||
**Basic Relay Settings:**
|
||||
- `relay_name`: Relay name (displayed in NIP-11)
|
||||
- `relay_description`: Relay description text
|
||||
- `relay_contact`: Contact information
|
||||
- `relay_software`: Software URL
|
||||
- `relay_version`: Software version
|
||||
- `supported_nips`: Comma-separated list of supported NIP numbers (e.g., "1,2,4,9,11,12,13,15,16,20,22,33,40,42")
|
||||
- `language_tags`: Comma-separated list of supported language tags (e.g., "en,es,fr" or "*" for all)
|
||||
- `relay_countries`: Comma-separated list of supported country codes (e.g., "US,CA,MX" or "*" for all)
|
||||
- `posting_policy`: Posting policy URL or text
|
||||
- `payments_url`: Payment URL for premium features
|
||||
- `max_connections`: Maximum concurrent connections
|
||||
- `max_subscriptions_per_client`: Max subscriptions per client
|
||||
- `max_event_tags`: Maximum tags per event
|
||||
- `max_content_length`: Maximum event content length
|
||||
### Common Settings
|
||||
|
||||
**Authentication & Access Control:**
|
||||
- `auth_enabled`: Enable whitelist/blacklist auth rules (`true`/`false`)
|
||||
- `nip42_auth_required`: Enable NIP-42 cryptographic authentication (`true`/`false`)
|
||||
- `nip42_auth_required_kinds`: Event kinds requiring NIP-42 auth (comma-separated)
|
||||
- `nip42_challenge_timeout`: NIP-42 challenge expiration seconds
|
||||
- **Relay Information**: Name, description, contact info
|
||||
- **Connection Limits**: Max subscriptions per client, total subscriptions
|
||||
- **Authentication**: Enable/disable NIP-42, whitelist/blacklist rules
|
||||
- **Proof of Work**: Minimum difficulty, enforcement mode
|
||||
- **Event Validation**: Max tags, content length, message size
|
||||
- **Expiration**: Enable/disable NIP-40 event expiration
|
||||
|
||||
**Proof of Work & Validation:**
|
||||
- `pow_min_difficulty`: Minimum proof-of-work difficulty
|
||||
- `nip40_expiration_enabled`: Enable event expiration (`true`/`false`)
|
||||
### Dynamic Updates
|
||||
|
||||
**Monitoring Settings:**
|
||||
- `kind_24567_reporting_throttle_sec`: Minimum seconds between monitoring events (default: 5)
|
||||
Most configuration changes take effect immediately without restart:
|
||||
- Relay information (NIP-11)
|
||||
- Authentication settings
|
||||
- Subscription limits
|
||||
- Event validation rules
|
||||
- Proof of Work settings
|
||||
|
||||
### Dynamic Configuration Updates
|
||||
See [`docs/configuration_guide.md`](docs/configuration_guide.md) for complete configuration reference.
|
||||
|
||||
C-Relay supports **dynamic configuration updates** without requiring a restart for most settings. Configuration parameters are categorized as either **dynamic** (can be updated immediately) or **restart-required** (require relay restart to take effect).
|
||||
## 📚 Documentation
|
||||
|
||||
**Dynamic Configuration Parameters (No Restart Required):**
|
||||
- All relay information (NIP-11) settings: `relay_name`, `relay_description`, `relay_contact`, `relay_software`, `relay_version`, `supported_nips`, `language_tags`, `relay_countries`, `posting_policy`, `payments_url`
|
||||
- Authentication settings: `auth_enabled`, `nip42_auth_required`, `nip42_auth_required_kinds`, `nip42_challenge_timeout`
|
||||
- Subscription limits: `max_subscriptions_per_client`, `max_total_subscriptions`
|
||||
- Event validation limits: `max_event_tags`, `max_content_length`, `max_message_length`
|
||||
- Proof of Work settings: `pow_min_difficulty`, `pow_mode`
|
||||
- Event expiration settings: `nip40_expiration_enabled`, `nip40_expiration_strict`, `nip40_expiration_filter`, `nip40_expiration_grace_period`
|
||||
- **[API Documentation](API.md)** - Complete API reference and advanced features
|
||||
- **[Configuration Guide](docs/configuration_guide.md)** - Detailed configuration options
|
||||
- **[Deployment Guide](docs/deployment_guide.md)** - Production deployment instructions
|
||||
- **[User Guide](docs/user_guide.md)** - End-user documentation
|
||||
- **[NIP-42 Authentication](docs/NIP-42_Authentication.md)** - Authentication setup guide
|
||||
|
||||
**Restart-Required Configuration Parameters:**
|
||||
- Connection settings: `max_connections`, `relay_port`
|
||||
- Database and core system settings
|
||||
## 🧪 Testing
|
||||
|
||||
When updating configuration, the admin API response will indicate whether a restart is required for each parameter. Dynamic updates take effect immediately and are reflected in NIP-11 relay information documents without restart.
|
||||
Run the comprehensive test suite:
|
||||
|
||||
### Response Format
|
||||
```bash
|
||||
# Run all tests
|
||||
./tests/run_all_tests.sh
|
||||
|
||||
All admin commands return **signed EVENT responses** via WebSocket following standard Nostr protocol. Responses use JSON content with structured data.
|
||||
# Run NIP-specific tests
|
||||
./tests/run_nip_tests.sh
|
||||
|
||||
#### Response Examples
|
||||
|
||||
**Success Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"status\": \"success\", \"message\": \"Operation completed successfully\", \"timestamp\": 1234567890}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
# Test specific NIPs
|
||||
./tests/42_nip_test.sh # NIP-42 authentication
|
||||
./tests/13_nip_test.sh # NIP-13 proof of work
|
||||
```
|
||||
|
||||
**Error Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"status\": \"error\", \"error\": \"invalid configuration value\", \"timestamp\": 1234567890}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
## 🔒 Security
|
||||
|
||||
### Admin Key Management
|
||||
|
||||
The admin private key is displayed **only once** during first startup. Store it securely:
|
||||
|
||||
```bash
|
||||
# Save to secure location
|
||||
echo "ADMIN_PRIVKEY=your_admin_private_key" > ~/.c-relay-admin
|
||||
chmod 600 ~/.c-relay-admin
|
||||
```
|
||||
|
||||
**Auth Rules Query Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"auth_rules_all\", \"total_results\": 2, \"timestamp\": 1234567890, \"data\": [{\"rule_type\": \"blacklist\", \"pattern_type\": \"pubkey\", \"pattern_value\": \"abc123...\", \"action\": \"allow\"}]}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
### Production Security
|
||||
|
||||
**Configuration Query Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"config_all\", \"total_results\": 27, \"timestamp\": 1234567890, \"data\": [{\"key\": \"auth_enabled\", \"value\": \"false\", \"data_type\": \"boolean\", \"category\": \"auth\", \"description\": \"Enable NIP-42 authentication\"}, {\"key\": \"relay_description\", \"value\": \"My Relay\", \"data_type\": \"string\", \"category\": \"relay\", \"description\": \"Relay description text\"}]}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
- Use HTTPS/WSS with reverse proxy (nginx/Apache)
|
||||
- Enable NIP-42 authentication for client verification
|
||||
- Configure whitelist/blacklist for access control
|
||||
- Set up firewall rules to restrict access
|
||||
- Enable Proof of Work to prevent spam
|
||||
- Regular database backups
|
||||
|
||||
**Configuration Update Success Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"total_results\": 2, \"timestamp\": 1234567890, \"status\": \"success\", \"data\": [{\"key\": \"auth_enabled\", \"value\": \"true\", \"status\": \"updated\"}, {\"key\": \"relay_description\", \"value\": \"My Updated Relay\", \"status\": \"updated\"}]}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
See [`docs/deployment_guide.md`](docs/deployment_guide.md#security-hardening) for security hardening guide.
|
||||
|
||||
**Configuration Update Error Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"config_update\", \"status\": \"error\", \"error\": \"field validation failed: invalid port number '99999' (must be 1-65535)\", \"timestamp\": 1234567890}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
## 🤝 Contributing
|
||||
|
||||
**Database Statistics Query Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"stats_query\", \"timestamp\": 1234567890, \"database_size_bytes\": 1048576, \"total_events\": 15432, \"database_created_at\": 1234567800, \"latest_event_at\": 1234567890, \"event_kinds\": [{\"kind\": 1, \"count\": 12000, \"percentage\": 77.8}, {\"kind\": 0, \"count\": 2500, \"percentage\": 16.2}], \"time_stats\": {\"total\": 15432, \"last_24h\": 234, \"last_7d\": 1456, \"last_30d\": 5432}, \"top_pubkeys\": [{\"pubkey\": \"abc123...\", \"event_count\": 1234, \"percentage\": 8.0}, {\"pubkey\": \"def456...\", \"event_count\": 987, \"percentage\": 6.4}]}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
Contributions are welcome! Please:
|
||||
|
||||
**SQL Query Response:**
|
||||
```json
|
||||
["EVENT", "temp_sub_id", {
|
||||
"id": "response_event_id",
|
||||
"pubkey": "relay_public_key",
|
||||
"created_at": 1234567890,
|
||||
"kind": 23457,
|
||||
"content": "nip44 encrypted:{\"query_type\": \"sql_query\", \"request_id\": \"request_event_id\", \"timestamp\": 1234567890, \"query\": \"SELECT * FROM events LIMIT 10\", \"execution_time_ms\": 45, \"row_count\": 10, \"columns\": [\"id\", \"pubkey\", \"created_at\", \"kind\", \"content\"], \"rows\": [[\"abc123...\", \"def456...\", 1234567890, 1, \"Hello world\"], ...]}",
|
||||
"tags": [
|
||||
["p", "admin_public_key"],
|
||||
["e", "request_event_id"]
|
||||
],
|
||||
"sig": "response_event_signature"
|
||||
}]
|
||||
```
|
||||
1. Fork the repository
|
||||
2. Create a feature branch
|
||||
3. Make your changes with tests
|
||||
4. Submit a pull request
|
||||
|
||||
### SQL Query Command
|
||||
## 📄 License
|
||||
|
||||
The `sql_query` command allows administrators to execute read-only SQL queries against the relay database. This provides powerful analytics and debugging capabilities through the admin API.
|
||||
[Add your license here]
|
||||
|
||||
**Request/Response Correlation:**
|
||||
- Each response includes the request event ID in both the `tags` array (`["e", "request_event_id"]`) and the decrypted content (`"request_id": "request_event_id"`)
|
||||
- This allows proper correlation when multiple queries are submitted concurrently
|
||||
- Frontend can track pending queries and match responses to requests
|
||||
## 🔗 Links
|
||||
|
||||
**Security Features:**
|
||||
- Only SELECT statements allowed (INSERT, UPDATE, DELETE, DROP, etc. are blocked)
|
||||
- Query timeout: 5 seconds (configurable)
|
||||
- Result row limit: 1000 rows (configurable)
|
||||
- All queries logged with execution time
|
||||
- **Repository**: [https://github.com/your-org/c-relay](https://github.com/your-org/c-relay)
|
||||
- **Releases**: [https://git.laantungir.net/laantungir/c-relay/releases](https://git.laantungir.net/laantungir/c-relay/releases)
|
||||
- **Issues**: [https://github.com/your-org/c-relay/issues](https://github.com/your-org/c-relay/issues)
|
||||
- **Nostr Protocol**: [https://github.com/nostr-protocol/nostr](https://github.com/nostr-protocol/nostr)
|
||||
|
||||
**Available Tables and Views:**
|
||||
- `events` - All Nostr events
|
||||
- `config` - Configuration parameters
|
||||
- `auth_rules` - Authentication rules
|
||||
- `subscription_events` - Subscription lifecycle log
|
||||
- `event_broadcasts` - Event broadcast log
|
||||
- `recent_events` - Last 1000 events (view)
|
||||
- `event_stats` - Event statistics by type (view)
|
||||
- `subscription_analytics` - Subscription metrics (view)
|
||||
- `active_subscriptions_log` - Currently active subscriptions (view)
|
||||
- `event_kinds_view` - Event distribution by kind (view)
|
||||
- `top_pubkeys_view` - Top 10 pubkeys by event count (view)
|
||||
- `time_stats_view` - Time-based statistics (view)
|
||||
## 💬 Support
|
||||
|
||||
**Example Queries:**
|
||||
```sql
|
||||
-- Recent events
|
||||
SELECT id, pubkey, created_at, kind FROM events ORDER BY created_at DESC LIMIT 20
|
||||
|
||||
-- Event distribution by kind
|
||||
SELECT * FROM event_kinds_view ORDER BY count DESC
|
||||
|
||||
-- Active subscriptions
|
||||
SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
|
||||
-- Database statistics
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM events) as total_events,
|
||||
(SELECT COUNT(*) FROM subscription_events) as total_subscriptions
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Real-time Monitoring System
|
||||
|
||||
C-Relay includes a subscription-based monitoring system that broadcasts real-time relay statistics using ephemeral events (kind 24567).
|
||||
|
||||
### Activation
|
||||
|
||||
The monitoring system activates automatically when clients subscribe to kind 24567 events:
|
||||
|
||||
```json
|
||||
["REQ", "monitoring-sub", {"kinds": [24567]}]
|
||||
```
|
||||
|
||||
For specific monitoring types, use d-tag filters:
|
||||
|
||||
```json
|
||||
["REQ", "event-kinds-sub", {"kinds": [24567], "#d": ["event_kinds"]}]
|
||||
["REQ", "time-stats-sub", {"kinds": [24567], "#d": ["time_stats"]}]
|
||||
["REQ", "top-pubkeys-sub", {"kinds": [24567], "#d": ["top_pubkeys"]}]
|
||||
```
|
||||
|
||||
When no subscriptions exist, monitoring is dormant to conserve resources.
|
||||
|
||||
### Monitoring Event Types
|
||||
|
||||
| Type | d Tag | Description |
|
||||
|------|-------|-------------|
|
||||
| Event Distribution | `event_kinds` | Event count by kind with percentages |
|
||||
| Time Statistics | `time_stats` | Events in last 24h, 7d, 30d |
|
||||
| Top Publishers | `top_pubkeys` | Top 10 pubkeys by event count |
|
||||
| Active Subscriptions | `active_subscriptions` | Current subscription details (admin only) |
|
||||
| Subscription Details | `subscription_details` | Detailed subscription info (admin only) |
|
||||
| CPU Metrics | `cpu_metrics` | Process CPU and memory usage |
|
||||
|
||||
### Event Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": 24567,
|
||||
"pubkey": "<relay_pubkey>",
|
||||
"created_at": <timestamp>,
|
||||
"content": "{\"data_type\":\"event_kinds\",\"timestamp\":1234567890,...}",
|
||||
"tags": [
|
||||
["d", "event_kinds"]
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
- `kind_24567_reporting_throttle_sec`: Minimum seconds between monitoring events (default: 5)
|
||||
|
||||
### Web Dashboard Integration
|
||||
|
||||
The built-in web dashboard (`/api/`) automatically subscribes to monitoring events and displays real-time statistics.
|
||||
|
||||
### Performance Considerations
|
||||
|
||||
- Monitoring events are ephemeral (not stored in database)
|
||||
- Throttling prevents excessive event generation
|
||||
- Automatic activation/deactivation based on subscriptions
|
||||
- Minimal overhead when no clients are monitoring
|
||||
|
||||
## Direct Messaging Admin System
|
||||
|
||||
In addition to the above admin API, c-relay allows the administrator to direct message the relay to get information or control some settings. As long as the administrator is signed in with any nostr client that allows sending nip-17 direct messages (DMs), they can control the relay.
|
||||
|
||||
The is possible because the relay is a full nostr citizen with it's own private and public key, and it knows the administrator's public key.
|
||||
|
||||
|
||||
|
||||
**Available DM commands**
|
||||
|
||||
The intent is not to be strict in the formatting of the DM. So for example if the relay receives any DM from the administrator with the words "stats" or "statistics" in it, it will respond to the administrator with a reply DM with the current relay statistics.
|
||||
|
||||
- `stats`|`statistics`: Relay statistics
|
||||
- `config`|`configuration`: Relay configuration
|
||||
- Open an issue on GitHub
|
||||
- Join the Nostr community
|
||||
- Contact via Nostr DM (relay pubkey shown on startup)
|
||||
|
||||
---
|
||||
|
||||
**Built with ❤️ for the Nostr protocol**
|
||||
|
||||
174
Real-Time Traffic Monitoring Commands.md
Normal file
174
Real-Time Traffic Monitoring Commands.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# Real-Time Traffic Monitoring Commands (Direct Server Use)
|
||||
|
||||
Copy and paste these commands directly on your server.
|
||||
|
||||
## Quick Status Checks
|
||||
|
||||
### See IPs visiting in the last few minutes:
|
||||
```bash
|
||||
sudo tail -500 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -20
|
||||
```
|
||||
|
||||
### See what status codes they're getting:
|
||||
```bash
|
||||
sudo tail -500 /var/log/nginx/access.log | awk '{print $1, $9}' | grep '216.73.216.38'
|
||||
```
|
||||
|
||||
### Count status codes (200 vs 403):
|
||||
```bash
|
||||
sudo tail -500 /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c
|
||||
```
|
||||
|
||||
## Real-Time Monitoring
|
||||
|
||||
### Watch live traffic (updates every 2 seconds):
|
||||
```bash
|
||||
watch -n 2 'sudo tail -200 /var/log/nginx/access.log | awk "{print \$1}" | sort | uniq -c | sort -rn | head -15'
|
||||
```
|
||||
|
||||
### See live log entries as they happen:
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/access.log
|
||||
```
|
||||
|
||||
### Live GoAccess dashboard:
|
||||
```bash
|
||||
sudo tail -f /var/log/nginx/access.log | goaccess -
|
||||
```
|
||||
|
||||
## Active Connections
|
||||
|
||||
### See who's connected RIGHT NOW:
|
||||
```bash
|
||||
sudo netstat -tn | grep ':443' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
### Alternative (using ss command):
|
||||
```bash
|
||||
sudo ss -tn | grep ':443' | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
## Detailed Analysis
|
||||
|
||||
### Last 100 requests with timestamps:
|
||||
```bash
|
||||
sudo tail -100 /var/log/nginx/access.log | awk '{print $4, $1}' | sed 's/\[//'
|
||||
```
|
||||
|
||||
### See what blocked IPs are trying to access:
|
||||
```bash
|
||||
sudo tail -500 /var/log/nginx/access.log | grep '216.73.216.38' | awk '{print $7}' | head -10
|
||||
```
|
||||
|
||||
### Show all 403 (blocked) requests:
|
||||
```bash
|
||||
sudo tail -500 /var/log/nginx/access.log | awk '$9==403 {print $1}' | sort | uniq -c | sort -rn
|
||||
```
|
||||
|
||||
### Show all successful (200) requests:
|
||||
```bash
|
||||
sudo tail -500 /var/log/nginx/access.log | awk '$9==200 {print $1}' | sort | uniq -c | sort -rn | head -10
|
||||
```
|
||||
|
||||
## Comprehensive Monitoring Script
|
||||
|
||||
### Create a monitoring script:
|
||||
```bash
|
||||
cat > /tmp/monitor-traffic.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
echo "=== Traffic in last 5 minutes ==="
|
||||
echo "Time: $(date)"
|
||||
echo ""
|
||||
echo "Top IPs:"
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
|
||||
echo ""
|
||||
echo "Blocked IPs (403 errors):"
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk '$9==403 {print $1}' | sort | uniq -c | sort -rn
|
||||
echo ""
|
||||
echo "Successful requests (200):"
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk '$9==200 {print $1}' | sort | uniq -c | sort -rn | head -5
|
||||
echo ""
|
||||
echo "Status Code Summary:"
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c | sort -rn
|
||||
EOF
|
||||
chmod +x /tmp/monitor-traffic.sh
|
||||
```
|
||||
|
||||
### Run the monitoring script:
|
||||
```bash
|
||||
/tmp/monitor-traffic.sh
|
||||
```
|
||||
|
||||
## Auto-Refreshing Dashboard
|
||||
|
||||
### Live dashboard (refreshes every 5 seconds):
|
||||
```bash
|
||||
watch -n 5 'echo "=== Last 5 minutes ==="
|
||||
date
|
||||
echo ""
|
||||
echo "Top IPs:"
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk "{print \$1}" | sort | uniq -c | sort -rn | head -10
|
||||
echo ""
|
||||
echo "Status Codes:"
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk "{print \$9}" | sort | uniq -c | sort -rn'
|
||||
```
|
||||
|
||||
Press `Ctrl+C` to exit.
|
||||
|
||||
## GoAccess HTML Report (Live Updating)
|
||||
|
||||
### Generate live HTML report:
|
||||
```bash
|
||||
sudo goaccess /var/log/nginx/access.log -o /var/www/html/live-stats.html --real-time-html --daemonize
|
||||
```
|
||||
|
||||
Then visit: https://git.laantungir.net/live-stats.html
|
||||
|
||||
### Stop the live report:
|
||||
```bash
|
||||
sudo pkill -f "goaccess.*live-stats"
|
||||
```
|
||||
|
||||
## Filter by Time
|
||||
|
||||
### Get timestamp from 5 minutes ago:
|
||||
```bash
|
||||
date -d '5 minutes ago' '+%d/%b/%Y:%H:%M'
|
||||
```
|
||||
|
||||
### Analyze only recent logs (replace timestamp):
|
||||
```bash
|
||||
sudo awk '/01\/Feb\/2026:19:09/,0' /var/log/nginx/access.log | goaccess -
|
||||
```
|
||||
|
||||
## Check Gitea CPU
|
||||
|
||||
### Current CPU usage:
|
||||
```bash
|
||||
ps aux | grep gitea | grep -v grep
|
||||
```
|
||||
|
||||
### Watch CPU in real-time:
|
||||
```bash
|
||||
watch -n 2 'ps aux | grep gitea | grep -v grep'
|
||||
```
|
||||
|
||||
## Most Useful Command for Quick Check
|
||||
|
||||
This one-liner shows everything you need:
|
||||
```bash
|
||||
|
||||
echo "=== Quick Status ===" && \
|
||||
echo "Time: $(date)" && \
|
||||
echo "" && \
|
||||
echo "Top 10 IPs (last 1000 requests):" && \
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10 && \
|
||||
echo "" && \
|
||||
echo "Status Codes:" && \
|
||||
sudo tail -1000 /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c && \
|
||||
echo "" && \
|
||||
echo "Gitea CPU:" && \
|
||||
ps aux | grep gitea | grep -v grep
|
||||
```
|
||||
|
||||
Copy any of these commands and run them directly on your server!
|
||||
612
STATIC_MUSL_GUIDE.md
Normal file
612
STATIC_MUSL_GUIDE.md
Normal file
@@ -0,0 +1,612 @@
|
||||
# Static MUSL Build Guide for C Programs
|
||||
|
||||
## Overview
|
||||
|
||||
This guide explains how to build truly portable static binaries using Alpine Linux and MUSL libc. These binaries have **zero runtime dependencies** and work on any Linux distribution without modification.
|
||||
|
||||
This guide is specifically tailored for C programs that use:
|
||||
- **nostr_core_lib** - Nostr protocol implementation
|
||||
- **nostr_login_lite** - Nostr authentication library
|
||||
- Common dependencies: libwebsockets, OpenSSL, SQLite, curl, secp256k1
|
||||
|
||||
## Why MUSL Static Binaries?
|
||||
|
||||
### Advantages Over glibc
|
||||
|
||||
| Feature | MUSL Static | glibc Static | glibc Dynamic |
|
||||
|---------|-------------|--------------|---------------|
|
||||
| **Portability** | ✓ Any Linux | ⚠ glibc only | ✗ Requires matching libs |
|
||||
| **Binary Size** | ~7-10 MB | ~12-15 MB | ~2-3 MB |
|
||||
| **Dependencies** | None | NSS libs | Many system libs |
|
||||
| **Deployment** | Single file | Single file + NSS | Binary + libraries |
|
||||
| **Compatibility** | Universal | glibc version issues | Library version hell |
|
||||
|
||||
### Key Benefits
|
||||
|
||||
1. **True Portability**: Works on Alpine, Ubuntu, Debian, CentOS, Arch, etc.
|
||||
2. **No Library Hell**: No `GLIBC_2.XX not found` errors
|
||||
3. **Simple Deployment**: Just copy one file
|
||||
4. **Reproducible Builds**: Same Docker image = same binary
|
||||
5. **Security**: No dependency on system libraries with vulnerabilities
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker installed and running
|
||||
- Your C project with source code
|
||||
- Internet connection for downloading dependencies
|
||||
|
||||
### Basic Build Process
|
||||
|
||||
```bash
|
||||
# 1. Copy the Dockerfile template (see below)
|
||||
cp /path/to/c-relay/Dockerfile.alpine-musl ./Dockerfile.static
|
||||
|
||||
# 2. Customize for your project (see Customization section)
|
||||
vim Dockerfile.static
|
||||
|
||||
# 3. Build the static binary
|
||||
docker build --platform linux/amd64 -f Dockerfile.static -t my-app-builder .
|
||||
|
||||
# 4. Extract the binary
|
||||
docker create --name temp-container my-app-builder
|
||||
docker cp temp-container:/build/my_app_static ./my_app_static
|
||||
docker rm temp-container
|
||||
|
||||
# 5. Verify it's static
|
||||
ldd ./my_app_static # Should show "not a dynamic executable"
|
||||
```
|
||||
|
||||
## Dockerfile Template
|
||||
|
||||
Here's a complete Dockerfile template you can customize for your project:
|
||||
|
||||
```dockerfile
|
||||
# Alpine-based MUSL static binary builder
|
||||
# Produces truly portable binaries with zero runtime dependencies
|
||||
|
||||
FROM alpine:3.19 AS builder
|
||||
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
musl-dev \
|
||||
git \
|
||||
cmake \
|
||||
pkgconfig \
|
||||
autoconf \
|
||||
automake \
|
||||
libtool \
|
||||
openssl-dev \
|
||||
openssl-libs-static \
|
||||
zlib-dev \
|
||||
zlib-static \
|
||||
curl-dev \
|
||||
curl-static \
|
||||
sqlite-dev \
|
||||
sqlite-static \
|
||||
linux-headers \
|
||||
wget \
|
||||
bash
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Build libsecp256k1 static (required for Nostr)
|
||||
RUN cd /tmp && \
|
||||
git clone https://github.com/bitcoin-core/secp256k1.git && \
|
||||
cd secp256k1 && \
|
||||
./autogen.sh && \
|
||||
./configure --enable-static --disable-shared --prefix=/usr \
|
||||
CFLAGS="-fPIC" && \
|
||||
make -j$(nproc) && \
|
||||
make install && \
|
||||
rm -rf /tmp/secp256k1
|
||||
|
||||
# Build libwebsockets static (if needed for WebSocket support)
|
||||
RUN cd /tmp && \
|
||||
git clone --depth 1 --branch v4.3.3 https://github.com/warmcat/libwebsockets.git && \
|
||||
cd libwebsockets && \
|
||||
mkdir build && cd build && \
|
||||
cmake .. \
|
||||
-DLWS_WITH_STATIC=ON \
|
||||
-DLWS_WITH_SHARED=OFF \
|
||||
-DLWS_WITH_SSL=ON \
|
||||
-DLWS_WITHOUT_TESTAPPS=ON \
|
||||
-DLWS_WITHOUT_TEST_SERVER=ON \
|
||||
-DLWS_WITHOUT_TEST_CLIENT=ON \
|
||||
-DLWS_WITHOUT_TEST_PING=ON \
|
||||
-DLWS_WITH_HTTP2=OFF \
|
||||
-DLWS_WITH_LIBUV=OFF \
|
||||
-DLWS_WITH_LIBEVENT=OFF \
|
||||
-DLWS_IPV6=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=/usr \
|
||||
-DCMAKE_C_FLAGS="-fPIC" && \
|
||||
make -j$(nproc) && \
|
||||
make install && \
|
||||
rm -rf /tmp/libwebsockets
|
||||
|
||||
# Copy git configuration for submodules
|
||||
COPY .gitmodules /build/.gitmodules
|
||||
COPY .git /build/.git
|
||||
|
||||
# Initialize submodules
|
||||
RUN git submodule update --init --recursive
|
||||
|
||||
# Copy and build nostr_core_lib
|
||||
COPY nostr_core_lib /build/nostr_core_lib/
|
||||
RUN cd nostr_core_lib && \
|
||||
chmod +x build.sh && \
|
||||
sed -i 's/CFLAGS="-Wall -Wextra -std=c99 -fPIC -O2"/CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -std=c99 -fPIC -O2"/' build.sh && \
|
||||
rm -f *.o *.a 2>/dev/null || true && \
|
||||
./build.sh --nips=1,6,13,17,19,44,59
|
||||
|
||||
# Copy and build nostr_login_lite (if used)
|
||||
# COPY nostr_login_lite /build/nostr_login_lite/
|
||||
# RUN cd nostr_login_lite && make static
|
||||
|
||||
# Copy your application source
|
||||
COPY src/ /build/src/
|
||||
COPY Makefile /build/Makefile
|
||||
|
||||
# Build your application with full static linking
|
||||
RUN gcc -static -O2 -Wall -Wextra -std=c99 \
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
|
||||
-I. -Inostr_core_lib -Inostr_core_lib/nostr_core \
|
||||
-Inostr_core_lib/cjson -Inostr_core_lib/nostr_websocket \
|
||||
src/*.c \
|
||||
-o /build/my_app_static \
|
||||
nostr_core_lib/libnostr_core_x64.a \
|
||||
-lwebsockets -lssl -lcrypto -lsqlite3 -lsecp256k1 \
|
||||
-lcurl -lz -lpthread -lm -ldl && \
|
||||
strip /build/my_app_static
|
||||
|
||||
# Verify it's truly static
|
||||
RUN echo "=== Binary Information ===" && \
|
||||
file /build/my_app_static && \
|
||||
ls -lh /build/my_app_static && \
|
||||
echo "=== Checking for dynamic dependencies ===" && \
|
||||
(ldd /build/my_app_static 2>&1 || echo "Binary is static")
|
||||
|
||||
# Output stage - just the binary
|
||||
FROM scratch AS output
|
||||
COPY --from=builder /build/my_app_static /my_app_static
|
||||
```
|
||||
|
||||
## Customization Guide
|
||||
|
||||
### 1. Adjust Dependencies
|
||||
|
||||
**Add dependencies** by modifying the `apk add` section:
|
||||
|
||||
```dockerfile
|
||||
RUN apk add --no-cache \
|
||||
build-base \
|
||||
musl-dev \
|
||||
# Add your dependencies here:
|
||||
libpng-dev \
|
||||
libpng-static \
|
||||
libjpeg-turbo-dev \
|
||||
libjpeg-turbo-static
|
||||
```
|
||||
|
||||
**Remove unused dependencies** to speed up builds:
|
||||
- Remove `libwebsockets` section if you don't need WebSocket support
|
||||
- Remove `sqlite` if you don't use databases
|
||||
- Remove `curl` if you don't make HTTP requests
|
||||
|
||||
### 2. Configure nostr_core_lib NIPs
|
||||
|
||||
Specify which NIPs your application needs:
|
||||
|
||||
```bash
|
||||
./build.sh --nips=1,6,19 # Minimal: Basic protocol, keys, bech32
|
||||
./build.sh --nips=1,6,13,17,19,44,59 # Full: All common NIPs
|
||||
./build.sh --nips=all # Everything available
|
||||
```
|
||||
|
||||
**Common NIP combinations:**
|
||||
- **Basic client**: `1,6,19` (events, keys, bech32)
|
||||
- **With encryption**: `1,6,19,44` (add modern encryption)
|
||||
- **With DMs**: `1,6,17,19,44,59` (add private messages)
|
||||
- **Relay/server**: `1,6,13,17,19,42,44,59` (add PoW, auth)
|
||||
|
||||
### 3. Modify Compilation Flags
|
||||
|
||||
**For your application:**
|
||||
|
||||
```dockerfile
|
||||
RUN gcc -static -O2 -Wall -Wextra -std=c99 \
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \ # REQUIRED for MUSL
|
||||
-I. -Inostr_core_lib \ # Include paths
|
||||
src/*.c \ # Your source files
|
||||
-o /build/my_app_static \ # Output binary
|
||||
nostr_core_lib/libnostr_core_x64.a \ # Nostr library
|
||||
-lwebsockets -lssl -lcrypto \ # Link libraries
|
||||
-lsqlite3 -lsecp256k1 -lcurl \
|
||||
-lz -lpthread -lm -ldl
|
||||
```
|
||||
|
||||
**Debug build** (with symbols, no optimization):
|
||||
|
||||
```dockerfile
|
||||
RUN gcc -static -g -O0 -DDEBUG \
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
|
||||
# ... rest of flags
|
||||
```
|
||||
|
||||
### 4. Multi-Architecture Support
|
||||
|
||||
Build for different architectures:
|
||||
|
||||
```bash
|
||||
# x86_64 (Intel/AMD)
|
||||
docker build --platform linux/amd64 -f Dockerfile.static -t my-app-x86 .
|
||||
|
||||
# ARM64 (Apple Silicon, Raspberry Pi 4+)
|
||||
docker build --platform linux/arm64 -f Dockerfile.static -t my-app-arm64 .
|
||||
```
|
||||
|
||||
## Build Script Template
|
||||
|
||||
Create a `build_static.sh` script for convenience:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
BUILD_DIR="$SCRIPT_DIR/build"
|
||||
DOCKERFILE="$SCRIPT_DIR/Dockerfile.static"
|
||||
|
||||
# Detect architecture
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64)
|
||||
PLATFORM="linux/amd64"
|
||||
OUTPUT_NAME="my_app_static_x86_64"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
PLATFORM="linux/arm64"
|
||||
OUTPUT_NAME="my_app_static_arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown architecture: $ARCH"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Building for platform: $PLATFORM"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
# Build Docker image
|
||||
docker build \
|
||||
--platform "$PLATFORM" \
|
||||
-f "$DOCKERFILE" \
|
||||
-t my-app-builder:latest \
|
||||
--progress=plain \
|
||||
.
|
||||
|
||||
# Extract binary
|
||||
CONTAINER_ID=$(docker create my-app-builder:latest)
|
||||
docker cp "$CONTAINER_ID:/build/my_app_static" "$BUILD_DIR/$OUTPUT_NAME"
|
||||
docker rm "$CONTAINER_ID"
|
||||
|
||||
chmod +x "$BUILD_DIR/$OUTPUT_NAME"
|
||||
|
||||
echo "✓ Build complete: $BUILD_DIR/$OUTPUT_NAME"
|
||||
echo "✓ Size: $(du -h "$BUILD_DIR/$OUTPUT_NAME" | cut -f1)"
|
||||
|
||||
# Verify
|
||||
if ldd "$BUILD_DIR/$OUTPUT_NAME" 2>&1 | grep -q "not a dynamic executable"; then
|
||||
echo "✓ Binary is fully static"
|
||||
else
|
||||
echo "⚠ Warning: Binary may have dynamic dependencies"
|
||||
fi
|
||||
```
|
||||
|
||||
Make it executable:
|
||||
|
||||
```bash
|
||||
chmod +x build_static.sh
|
||||
./build_static.sh
|
||||
```
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue 1: Fortification Errors
|
||||
|
||||
**Error:**
|
||||
```
|
||||
undefined reference to '__snprintf_chk'
|
||||
undefined reference to '__fprintf_chk'
|
||||
```
|
||||
|
||||
**Cause**: GCC's `-O2` enables fortification by default, which uses glibc-specific functions.
|
||||
|
||||
**Solution**: Add these flags to **all** compilation commands:
|
||||
```bash
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
|
||||
```
|
||||
|
||||
This must be applied to:
|
||||
1. nostr_core_lib build.sh
|
||||
2. Your application compilation
|
||||
3. Any other libraries you build
|
||||
|
||||
### Issue 2: Missing Symbols from nostr_core_lib
|
||||
|
||||
**Error:**
|
||||
```
|
||||
undefined reference to 'nostr_create_event'
|
||||
undefined reference to 'nostr_sign_event'
|
||||
```
|
||||
|
||||
**Cause**: Required NIPs not included in nostr_core_lib build.
|
||||
|
||||
**Solution**: Add missing NIPs:
|
||||
```bash
|
||||
./build.sh --nips=1,6,19 # Add the NIPs you need
|
||||
```
|
||||
|
||||
### Issue 3: Docker Permission Denied
|
||||
|
||||
**Error:**
|
||||
```
|
||||
permission denied while trying to connect to the Docker daemon socket
|
||||
```
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker # Or logout and login
|
||||
```
|
||||
|
||||
### Issue 4: Binary Won't Run on Target System
|
||||
|
||||
**Checks**:
|
||||
```bash
|
||||
# 1. Verify it's static
|
||||
ldd my_app_static # Should show "not a dynamic executable"
|
||||
|
||||
# 2. Check architecture
|
||||
file my_app_static # Should match target system
|
||||
|
||||
# 3. Test on different distributions
|
||||
docker run --rm -v $(pwd):/app alpine:latest /app/my_app_static --version
|
||||
docker run --rm -v $(pwd):/app ubuntu:latest /app/my_app_static --version
|
||||
```
|
||||
|
||||
## Project Structure Example
|
||||
|
||||
Organize your project for easy static builds:
|
||||
|
||||
```
|
||||
my-nostr-app/
|
||||
├── src/
|
||||
│ ├── main.c
|
||||
│ ├── handlers.c
|
||||
│ └── utils.c
|
||||
├── nostr_core_lib/ # Git submodule
|
||||
├── nostr_login_lite/ # Git submodule (if used)
|
||||
├── Dockerfile.static # Static build Dockerfile
|
||||
├── build_static.sh # Build script
|
||||
├── Makefile # Regular build
|
||||
└── README.md
|
||||
```
|
||||
|
||||
### Makefile Integration
|
||||
|
||||
Add static build targets to your Makefile:
|
||||
|
||||
```makefile
|
||||
# Regular dynamic build
|
||||
all: my_app
|
||||
|
||||
my_app: src/*.c
|
||||
gcc -O2 src/*.c -o my_app \
|
||||
nostr_core_lib/libnostr_core_x64.a \
|
||||
-lssl -lcrypto -lsecp256k1 -lz -lpthread -lm
|
||||
|
||||
# Static MUSL build via Docker
|
||||
static:
|
||||
./build_static.sh
|
||||
|
||||
# Clean
|
||||
clean:
|
||||
rm -f my_app build/my_app_static_*
|
||||
|
||||
.PHONY: all static clean
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### Single Binary Deployment
|
||||
|
||||
```bash
|
||||
# Copy to server
|
||||
scp build/my_app_static_x86_64 user@server:/opt/my-app/
|
||||
|
||||
# Run (no dependencies needed!)
|
||||
ssh user@server
|
||||
/opt/my-app/my_app_static_x86_64
|
||||
```
|
||||
|
||||
### SystemD Service
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=My Nostr Application
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=myapp
|
||||
WorkingDirectory=/opt/my-app
|
||||
ExecStart=/opt/my-app/my_app_static_x86_64
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### Docker Container (Minimal)
|
||||
|
||||
```dockerfile
|
||||
FROM scratch
|
||||
COPY my_app_static_x86_64 /app
|
||||
ENTRYPOINT ["/app"]
|
||||
```
|
||||
|
||||
Build and run:
|
||||
```bash
|
||||
docker build -t my-app:latest .
|
||||
docker run --rm my-app:latest --help
|
||||
```
|
||||
|
||||
## Reusing c-relay Files
|
||||
|
||||
You can directly copy these files from c-relay:
|
||||
|
||||
### 1. Dockerfile.alpine-musl
|
||||
```bash
|
||||
cp /path/to/c-relay/Dockerfile.alpine-musl ./Dockerfile.static
|
||||
```
|
||||
|
||||
Then customize:
|
||||
- Change binary name (line 125)
|
||||
- Adjust source files (line 122-124)
|
||||
- Modify include paths (line 120-121)
|
||||
|
||||
### 2. build_static.sh
|
||||
```bash
|
||||
cp /path/to/c-relay/build_static.sh ./
|
||||
```
|
||||
|
||||
Then customize:
|
||||
- Change `OUTPUT_NAME` variable (lines 66, 70)
|
||||
- Update Docker image name (line 98)
|
||||
- Modify verification commands (lines 180-184)
|
||||
|
||||
### 3. .dockerignore (Optional)
|
||||
```bash
|
||||
cp /path/to/c-relay/.dockerignore ./
|
||||
```
|
||||
|
||||
Helps speed up Docker builds by excluding unnecessary files.
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Version Control**: Commit your Dockerfile and build script
|
||||
2. **Tag Builds**: Include git commit hash in binary version
|
||||
3. **Test Thoroughly**: Verify on multiple distributions
|
||||
4. **Document Dependencies**: List required NIPs and libraries
|
||||
5. **Automate**: Use CI/CD to build on every commit
|
||||
6. **Archive Binaries**: Keep old versions for rollback
|
||||
|
||||
## Performance Comparison
|
||||
|
||||
| Metric | MUSL Static | glibc Dynamic |
|
||||
|--------|-------------|---------------|
|
||||
| Binary Size | 7-10 MB | 2-3 MB + libs |
|
||||
| Startup Time | ~50ms | ~40ms |
|
||||
| Memory Usage | Similar | Similar |
|
||||
| Portability | ✓ Universal | ✗ System-dependent |
|
||||
| Deployment | Single file | Binary + libraries |
|
||||
|
||||
## References
|
||||
|
||||
- [MUSL libc](https://musl.libc.org/)
|
||||
- [Alpine Linux](https://alpinelinux.org/)
|
||||
- [nostr_core_lib](https://github.com/chebizarro/nostr_core_lib)
|
||||
- [Static Linking Best Practices](https://www.musl-libc.org/faq.html)
|
||||
- [c-relay Implementation](./docs/musl_static_build.md)
|
||||
|
||||
## Example: Minimal Nostr Client
|
||||
|
||||
Here's a complete example of building a minimal Nostr client:
|
||||
|
||||
```c
|
||||
// minimal_client.c
|
||||
#include "nostr_core/nostr_core.h"
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
// Generate keypair
|
||||
char nsec[64], npub[64];
|
||||
nostr_generate_keypair(nsec, npub);
|
||||
|
||||
printf("Generated keypair:\n");
|
||||
printf("Private key (nsec): %s\n", nsec);
|
||||
printf("Public key (npub): %s\n", npub);
|
||||
|
||||
// Create event
|
||||
cJSON *event = nostr_create_event(1, "Hello, Nostr!", NULL);
|
||||
nostr_sign_event(event, nsec);
|
||||
|
||||
char *json = cJSON_Print(event);
|
||||
printf("\nSigned event:\n%s\n", json);
|
||||
|
||||
free(json);
|
||||
cJSON_Delete(event);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
**Dockerfile.static:**
|
||||
```dockerfile
|
||||
FROM alpine:3.19 AS builder
|
||||
RUN apk add --no-cache build-base musl-dev git autoconf automake libtool \
|
||||
openssl-dev openssl-libs-static zlib-dev zlib-static
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
# Build secp256k1
|
||||
RUN cd /tmp && git clone https://github.com/bitcoin-core/secp256k1.git && \
|
||||
cd secp256k1 && ./autogen.sh && \
|
||||
./configure --enable-static --disable-shared --prefix=/usr CFLAGS="-fPIC" && \
|
||||
make -j$(nproc) && make install
|
||||
|
||||
# Copy and build nostr_core_lib
|
||||
COPY nostr_core_lib /build/nostr_core_lib/
|
||||
RUN cd nostr_core_lib && \
|
||||
sed -i 's/CFLAGS="-Wall/CFLAGS="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall/' build.sh && \
|
||||
./build.sh --nips=1,6,19
|
||||
|
||||
# Build application
|
||||
COPY minimal_client.c /build/
|
||||
RUN gcc -static -O2 -Wall -std=c99 \
|
||||
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 \
|
||||
-Inostr_core_lib -Inostr_core_lib/nostr_core -Inostr_core_lib/cjson \
|
||||
minimal_client.c -o /build/minimal_client_static \
|
||||
nostr_core_lib/libnostr_core_x64.a \
|
||||
-lssl -lcrypto -lsecp256k1 -lz -lpthread -lm -ldl && \
|
||||
strip /build/minimal_client_static
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /build/minimal_client_static /minimal_client_static
|
||||
```
|
||||
|
||||
**Build and run:**
|
||||
```bash
|
||||
docker build -f Dockerfile.static -t minimal-client .
|
||||
docker create --name temp minimal-client
|
||||
docker cp temp:/minimal_client_static ./
|
||||
docker rm temp
|
||||
|
||||
./minimal_client_static
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
Static MUSL binaries provide the best portability for C applications. While they're slightly larger than dynamic binaries, the benefits of zero dependencies and universal compatibility make them ideal for:
|
||||
|
||||
- Server deployments across different Linux distributions
|
||||
- Embedded systems and IoT devices
|
||||
- Docker containers (FROM scratch)
|
||||
- Distribution to users without dependency management
|
||||
- Long-term archival and reproducibility
|
||||
|
||||
Follow this guide to create portable, self-contained binaries for your Nostr applications!
|
||||
17
api/index.js
17
api/index.js
@@ -4324,6 +4324,12 @@ function populateSubscriptionDetailsTable(subscriptionsData) {
|
||||
const oldestDuration = Math.max(...subscriptions.map(s => now - s.created_at));
|
||||
const oldestDurationStr = formatDuration(oldestDuration);
|
||||
|
||||
// Calculate total query stats for this connection
|
||||
const totalQueries = subscriptions.reduce((sum, s) => sum + (s.db_queries_executed || 0), 0);
|
||||
const totalRows = subscriptions.reduce((sum, s) => sum + (s.db_rows_returned || 0), 0);
|
||||
const avgQueryRate = subscriptions.length > 0 ? (subscriptions[0].query_rate_per_min || 0) : 0;
|
||||
const clientIp = subscriptions.length > 0 ? (subscriptions[0].client_ip || 'unknown') : 'unknown';
|
||||
|
||||
// Create header row (summary)
|
||||
const headerRow = document.createElement('tr');
|
||||
headerRow.className = 'subscription-group-header';
|
||||
@@ -4334,9 +4340,14 @@ function populateSubscriptionDetailsTable(subscriptionsData) {
|
||||
headerRow.innerHTML = `
|
||||
<td colspan="4" style="padding: 8px;">
|
||||
<span class="expand-icon" style="display: inline-block; width: 20px; transition: transform 0.2s;">▶</span>
|
||||
<strong style="font-family: 'Courier New', monospace; font-size: 12px;">Websocket: ${wsiPointer}</strong>
|
||||
<span style="color: #666; margin-left: 15px;">
|
||||
Subscriptions: ${subCount} | Oldest: ${oldestDurationStr}
|
||||
<strong style="font-family: 'Courier New', monospace; font-size: 12px;">IP: ${clientIp}</strong>
|
||||
<span style="color: #666; margin-left: 10px; font-size: 11px;">
|
||||
WS: ${wsiPointer} |
|
||||
Subs: ${subCount} |
|
||||
Queries: ${totalQueries.toLocaleString()} |
|
||||
Rows: ${totalRows.toLocaleString()} |
|
||||
Rate: ${avgQueryRate.toFixed(1)} q/min |
|
||||
Duration: ${oldestDurationStr}
|
||||
</span>
|
||||
</td>
|
||||
`;
|
||||
|
||||
@@ -81,6 +81,29 @@ echo "Building for platform: $PLATFORM"
|
||||
echo "Output binary: $OUTPUT_NAME"
|
||||
echo ""
|
||||
|
||||
# Check if Alpine base image is cached
|
||||
echo "Checking for cached Alpine Docker image..."
|
||||
if ! docker images alpine:3.19 --format "{{.Repository}}:{{.Tag}}" | grep -q "alpine:3.19"; then
|
||||
echo "⚠ Alpine 3.19 image not found in cache"
|
||||
echo "Attempting to pull Alpine 3.19 image..."
|
||||
if ! docker pull alpine:3.19; then
|
||||
echo ""
|
||||
echo "ERROR: Failed to pull Alpine 3.19 image"
|
||||
echo "This is required for the static build."
|
||||
echo ""
|
||||
echo "Possible solutions:"
|
||||
echo " 1. Check your internet connection"
|
||||
echo " 2. Try again later (Docker Hub may be temporarily unavailable)"
|
||||
echo " 3. If you have IPv6 issues, disable IPv6 for Docker"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ Alpine 3.19 image pulled successfully"
|
||||
else
|
||||
echo "✓ Alpine 3.19 image found in cache"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Build the Docker image
|
||||
echo "=========================================="
|
||||
echo "Step 1: Building Alpine Docker image"
|
||||
|
||||
Binary file not shown.
BIN
c-relay-1.1.0.tar.gz
Normal file
BIN
c-relay-1.1.0.tar.gz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Submodule c_utils_lib updated: 442facd7e3...f46747b243
@@ -3,17 +3,17 @@
|
||||
# Copy the binary to the deployment location
|
||||
cp build/c_relay_x86 ~/Storage/c_relay/crelay
|
||||
|
||||
# Copy the local service file to systemd
|
||||
# sudo cp systemd/c-relay-local.service /etc/systemd/system/
|
||||
# Copy the service file to systemd (use the main service file)
|
||||
sudo cp systemd/c-relay.service /etc/systemd/system/c-relay-local.service
|
||||
|
||||
# Reload systemd daemon to pick up the new service
|
||||
# sudo systemctl daemon-reload
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# Enable the service (if not already enabled)
|
||||
# sudo systemctl enable c-relay-local.service
|
||||
sudo systemctl enable c-relay-local.service
|
||||
|
||||
# Restart the service
|
||||
# sudo systemctl restart c-relay-local.service
|
||||
sudo systemctl restart c-relay-local.service
|
||||
|
||||
# Show service status
|
||||
# sudo systemctl status c-relay-local.service --no-pager -l
|
||||
sudo systemctl status c-relay-local.service --no-pager -l
|
||||
|
||||
532
docs/subscription_cleanup_simplified.md
Normal file
532
docs/subscription_cleanup_simplified.md
Normal file
@@ -0,0 +1,532 @@
|
||||
# Subscription Cleanup - Simplified Design
|
||||
|
||||
## Problem Summary
|
||||
|
||||
The c-relay Nostr relay experienced severe performance degradation (90-100% CPU) due to subscription accumulation in the database. Investigation revealed **323,644 orphaned subscriptions** that were never properly closed when WebSocket connections dropped.
|
||||
|
||||
## Solution: Two-Component Approach
|
||||
|
||||
This simplified design focuses on two pragmatic solutions that align with Nostr's stateless design:
|
||||
|
||||
1. **Startup Cleanup**: Close all subscriptions on relay restart
|
||||
2. **Connection Age Limit**: Disconnect clients after a configurable time period
|
||||
|
||||
Both solutions force clients to reconnect and re-establish subscriptions, which is standard Nostr behavior.
|
||||
|
||||
---
|
||||
|
||||
## Component 1: Startup Cleanup
|
||||
|
||||
### Purpose
|
||||
Ensure clean state on every relay restart by closing all subscriptions in the database.
|
||||
|
||||
### Implementation
|
||||
|
||||
**File:** [`src/subscriptions.c`](src/subscriptions.c)
|
||||
|
||||
**New Function:**
|
||||
```c
|
||||
void cleanup_all_subscriptions_on_startup(void) {
|
||||
if (!g_db) {
|
||||
DEBUG_ERROR("Database not initialized for startup cleanup");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Startup cleanup: Marking all active subscriptions as disconnected");
|
||||
|
||||
// Mark all 'created' subscriptions as disconnected
|
||||
const char* update_sql =
|
||||
"UPDATE subscriptions "
|
||||
"SET ended_at = strftime('%s', 'now') "
|
||||
"WHERE event_type = 'created' AND ended_at IS NULL";
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
int rc = sqlite3_prepare_v2(g_db, update_sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare startup cleanup query: %s", sqlite3_errmsg(g_db));
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
int updated_count = sqlite3_changes(g_db);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (updated_count > 0) {
|
||||
// Log a single 'disconnected' event for the startup cleanup
|
||||
const char* insert_sql =
|
||||
"INSERT INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type) "
|
||||
"VALUES ('startup_cleanup', '', 'system', 'disconnected')";
|
||||
|
||||
rc = sqlite3_prepare_v2(g_db, insert_sql, -1, &stmt, NULL);
|
||||
if (rc == SQLITE_OK) {
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
DEBUG_LOG("Startup cleanup: Marked %d subscriptions as disconnected", updated_count);
|
||||
} else {
|
||||
DEBUG_LOG("Startup cleanup: No active subscriptions found");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Integration Point:** [`src/main.c:1810`](src/main.c:1810)
|
||||
|
||||
```c
|
||||
// Initialize subscription manager mutexes
|
||||
if (pthread_mutex_init(&g_subscription_manager.subscriptions_lock, NULL) != 0) {
|
||||
DEBUG_ERROR("Failed to initialize subscriptions mutex");
|
||||
sqlite3_close(g_db);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&g_subscription_manager.ip_tracking_lock, NULL) != 0) {
|
||||
DEBUG_ERROR("Failed to initialize IP tracking mutex");
|
||||
pthread_mutex_destroy(&g_subscription_manager.subscriptions_lock);
|
||||
sqlite3_close(g_db);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// **NEW: Startup cleanup - close all subscriptions**
|
||||
cleanup_all_subscriptions_on_startup();
|
||||
|
||||
// Start WebSocket relay server
|
||||
DEBUG_LOG("Starting WebSocket relay server...");
|
||||
if (start_websocket_relay(port_override, strict_port) != 0) {
|
||||
DEBUG_ERROR("Failed to start WebSocket relay");
|
||||
// ... cleanup code
|
||||
}
|
||||
```
|
||||
|
||||
### Benefits
|
||||
- **Immediate relief**: Restart relay to fix subscription issues
|
||||
- **Clean slate**: Every restart starts with zero active subscriptions
|
||||
- **Simple**: Single SQL UPDATE statement
|
||||
- **Nostr-aligned**: Clients are designed to reconnect after relay restart
|
||||
- **No configuration needed**: Always runs on startup
|
||||
|
||||
---
|
||||
|
||||
## Component 2: Connection Age Limit
|
||||
|
||||
### Purpose
|
||||
Automatically disconnect clients after a configurable time period, forcing them to reconnect and re-establish subscriptions.
|
||||
|
||||
### Why Disconnect Instead of Just Closing Subscriptions?
|
||||
|
||||
**Option 1: Send CLOSED message (keep connection)**
|
||||
- ❌ Not all clients handle `CLOSED` messages properly
|
||||
- ❌ Silent failure - client thinks it's subscribed but isn't
|
||||
- ❌ Partial cleanup - connection still consumes resources
|
||||
- ❌ More complex to implement
|
||||
|
||||
**Option 2: Disconnect client entirely (force reconnection)** ✅
|
||||
- ✅ Universal compatibility - all clients handle WebSocket reconnection
|
||||
- ✅ Complete resource cleanup (memory, file descriptors, etc.)
|
||||
- ✅ Simple implementation - single operation
|
||||
- ✅ Well-tested code path (same as network interruptions)
|
||||
- ✅ Forces re-authentication if needed
|
||||
|
||||
### Implementation
|
||||
|
||||
**File:** [`src/websockets.c`](src/websockets.c)
|
||||
|
||||
**New Function:**
|
||||
```c
|
||||
/**
|
||||
* Check connection age and disconnect clients that have been connected too long.
|
||||
* This forces clients to reconnect and re-establish subscriptions.
|
||||
*
|
||||
* Uses libwebsockets' lws_vhost_foreach_wsi() to iterate through all active
|
||||
* connections and checks their connection_established timestamp from per_session_data.
|
||||
*/
|
||||
void check_connection_age(int max_connection_seconds) {
|
||||
if (max_connection_seconds <= 0 || !ws_context) {
|
||||
return;
|
||||
}
|
||||
|
||||
time_t now = time(NULL);
|
||||
time_t cutoff = now - max_connection_seconds;
|
||||
|
||||
// Get the default vhost
|
||||
struct lws_vhost *vhost = lws_get_vhost_by_name(ws_context, "default");
|
||||
if (!vhost) {
|
||||
DEBUG_ERROR("Failed to get vhost for connection age check");
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate through all active WebSocket connections
|
||||
// Note: lws_vhost_foreach_wsi() calls our callback for each connection
|
||||
struct lws *wsi = NULL;
|
||||
while ((wsi = lws_vhost_foreach_wsi(vhost, wsi)) != NULL) {
|
||||
// Get per-session data which contains connection_established timestamp
|
||||
struct per_session_data *pss = (struct per_session_data *)lws_wsi_user(wsi);
|
||||
|
||||
if (pss && pss->connection_established > 0) {
|
||||
// Check if connection is older than cutoff
|
||||
if (pss->connection_established < cutoff) {
|
||||
// Connection is too old - close it
|
||||
long age_seconds = now - pss->connection_established;
|
||||
|
||||
DEBUG_LOG("Closing connection from %s (age: %lds, limit: %ds)",
|
||||
pss->client_ip, age_seconds, max_connection_seconds);
|
||||
|
||||
// Close with normal status and reason message
|
||||
lws_close_reason(wsi, LWS_CLOSE_STATUS_NORMAL,
|
||||
(unsigned char *)"connection age limit", 21);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Key Implementation Details:**
|
||||
|
||||
1. **No database needed**: Active connections are tracked by libwebsockets itself
|
||||
2. **Uses existing timestamp**: `pss->connection_established` is already set on line 456 of websockets.c
|
||||
3. **Built-in iterator**: `lws_vhost_foreach_wsi()` safely iterates through all active connections
|
||||
4. **Per-session data**: Each connection's `per_session_data` is accessible via `lws_wsi_user()`
|
||||
5. **Safe closure**: `lws_close_reason()` properly closes the WebSocket with a status code and message
|
||||
|
||||
**Integration Point:** [`src/websockets.c:2176`](src/websockets.c:2176) - in existing event loop
|
||||
|
||||
```c
|
||||
// Main event loop with proper signal handling
|
||||
while (g_server_running && !g_shutdown_flag) {
|
||||
int result = lws_service(ws_context, 1000);
|
||||
|
||||
if (result < 0) {
|
||||
DEBUG_ERROR("libwebsockets service error");
|
||||
break;
|
||||
}
|
||||
|
||||
// Check if it's time to post status update
|
||||
time_t current_time = time(NULL);
|
||||
int status_post_hours = get_config_int("kind_1_status_posts_hours", 0);
|
||||
|
||||
if (status_post_hours > 0) {
|
||||
int seconds_interval = status_post_hours * 3600;
|
||||
if (current_time - last_status_post_time >= seconds_interval) {
|
||||
last_status_post_time = current_time;
|
||||
generate_and_post_status_event();
|
||||
}
|
||||
}
|
||||
|
||||
// **NEW: Check for connection age limit**
|
||||
int max_connection_seconds = get_config_int("max_connection_seconds", 86400); // Default 24 hours
|
||||
if (max_connection_seconds > 0) {
|
||||
check_connection_age(max_connection_seconds);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
**Parameter:** `max_connection_seconds`
|
||||
- **Default:** `86400` (24 hours)
|
||||
- **Range:** `0` = disabled, `>0` = disconnect after X seconds
|
||||
- **Units:** Seconds (for consistency with other time-based configs)
|
||||
|
||||
**Example configurations:**
|
||||
```json
|
||||
{
|
||||
"max_connection_seconds": 86400 // 86400 seconds = 24 hours (default)
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"max_connection_seconds": 43200 // 43200 seconds = 12 hours
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"max_connection_seconds": 3600 // 3600 seconds = 1 hour
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"max_connection_seconds": 0 // Disabled
|
||||
}
|
||||
```
|
||||
|
||||
### Client Behavior
|
||||
|
||||
When disconnected due to age limit, clients will:
|
||||
1. Detect WebSocket closure
|
||||
2. Wait briefly (exponential backoff)
|
||||
3. Reconnect to relay
|
||||
4. Re-authenticate if needed (NIP-42)
|
||||
5. Re-establish all subscriptions
|
||||
6. Resume normal operation
|
||||
|
||||
This is **exactly what happens** during network interruptions, so it's a well-tested code path in all Nostr clients.
|
||||
|
||||
### Benefits
|
||||
- **No new threads**: Uses existing event loop
|
||||
- **Minimal overhead**: Check runs once per second (same as `lws_service`)
|
||||
- **Simple implementation**: Iterate through active connections
|
||||
- **Consistent pattern**: Matches existing status post checking
|
||||
- **Universal compatibility**: All clients handle reconnection
|
||||
- **Complete cleanup**: Frees all resources associated with connection
|
||||
- **Configurable**: Can be adjusted per relay needs or disabled entirely
|
||||
|
||||
---
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Startup Cleanup (1-2 hours)
|
||||
|
||||
1. **Add `cleanup_all_subscriptions_on_startup()` function**
|
||||
- File: [`src/subscriptions.c`](src/subscriptions.c)
|
||||
- SQL UPDATE to mark all active subscriptions as disconnected
|
||||
- Add logging for cleanup count
|
||||
|
||||
2. **Integrate in main()**
|
||||
- File: [`src/main.c:1810`](src/main.c:1810)
|
||||
- Call after mutex initialization, before WebSocket server start
|
||||
|
||||
3. **Test**
|
||||
- Create subscriptions in database
|
||||
- Restart relay
|
||||
- Verify all subscriptions marked as disconnected
|
||||
- Verify `active_subscriptions_log` shows 0 subscriptions
|
||||
|
||||
**Estimated Time:** 1-2 hours
|
||||
|
||||
### Phase 2: Connection Age Limit (2-3 hours)
|
||||
|
||||
1. **Add `check_connection_age()` function**
|
||||
- File: [`src/websockets.c`](src/websockets.c)
|
||||
- Iterate through active connections
|
||||
- Close connections older than limit
|
||||
|
||||
2. **Integrate in event loop**
|
||||
- File: [`src/websockets.c:2176`](src/websockets.c:2176)
|
||||
- Add check after status post check
|
||||
- Use same pattern as status posts
|
||||
|
||||
3. **Add configuration parameter**
|
||||
- Add `max_connection_seconds` to default config
|
||||
- Default: 86400 (24 hours)
|
||||
|
||||
4. **Test**
|
||||
- Connect client
|
||||
- Wait for timeout (or reduce timeout for testing)
|
||||
- Verify client disconnected
|
||||
- Verify client reconnects automatically
|
||||
- Verify subscriptions re-established
|
||||
|
||||
**Estimated Time:** 2-3 hours
|
||||
|
||||
---
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### Startup Cleanup Tests
|
||||
|
||||
```bash
|
||||
# Test 1: Clean startup with existing subscriptions
|
||||
- Create 100 active subscriptions in database
|
||||
- Restart relay
|
||||
- Verify all subscriptions marked as disconnected
|
||||
- Verify active_subscriptions_log shows 0 subscriptions
|
||||
|
||||
# Test 2: Clean startup with no subscriptions
|
||||
- Start relay with empty database
|
||||
- Verify no errors
|
||||
- Verify startup cleanup logs "No active subscriptions found"
|
||||
|
||||
# Test 3: Clients reconnect after restart
|
||||
- Create subscriptions before restart
|
||||
- Restart relay
|
||||
- Connect clients and create new subscriptions
|
||||
- Verify new subscriptions tracked correctly
|
||||
```
|
||||
|
||||
### Connection Age Limit Tests
|
||||
|
||||
```bash
|
||||
# Test 1: Connection disconnected after timeout
|
||||
- Set max_connection_seconds to 60 (for testing)
|
||||
- Connect client
|
||||
- Wait 61 seconds
|
||||
- Verify client disconnected
|
||||
- Verify client reconnects automatically
|
||||
|
||||
# Test 2: Subscriptions re-established after reconnection
|
||||
- Connect client with subscriptions
|
||||
- Wait for timeout
|
||||
- Verify client reconnects
|
||||
- Verify subscriptions re-established
|
||||
- Verify events still delivered
|
||||
|
||||
# Test 3: Disabled when set to 0
|
||||
- Set max_connection_seconds to 0
|
||||
- Connect client
|
||||
- Wait extended period
|
||||
- Verify client NOT disconnected
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
```bash
|
||||
# Test 1: Combined behavior
|
||||
- Start relay (startup cleanup runs)
|
||||
- Connect multiple clients
|
||||
- Create subscriptions
|
||||
- Wait for connection timeout
|
||||
- Verify clients reconnect
|
||||
- Restart relay
|
||||
- Verify clean state
|
||||
|
||||
# Test 2: Load test
|
||||
- Connect 100 clients
|
||||
- Each creates 5 subscriptions
|
||||
- Wait for connection timeout
|
||||
- Verify all clients reconnect
|
||||
- Verify all subscriptions re-established
|
||||
- Monitor CPU usage (should remain low)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Component 1: Startup Cleanup
|
||||
- ✅ Relay starts with zero active subscriptions
|
||||
- ✅ All previous subscriptions marked as disconnected on startup
|
||||
- ✅ Clients successfully reconnect and re-establish subscriptions
|
||||
- ✅ Relay restart can be used as emergency fix for subscription issues
|
||||
- ✅ No errors during startup cleanup process
|
||||
|
||||
### Component 2: Connection Age Limit
|
||||
- ✅ Clients disconnected after configured time period
|
||||
- ✅ Clients automatically reconnect
|
||||
- ✅ Subscriptions re-established after reconnection
|
||||
- ✅ No impact on relay performance
|
||||
- ✅ Configuration parameter works correctly (including disabled state)
|
||||
|
||||
### Overall Success
|
||||
- ✅ CPU usage remains low (<10%)
|
||||
- ✅ No orphaned subscriptions accumulate
|
||||
- ✅ Database size remains stable
|
||||
- ✅ No manual intervention required
|
||||
|
||||
---
|
||||
|
||||
## Configuration Reference
|
||||
|
||||
**New Configuration Parameters:**
|
||||
|
||||
```json
|
||||
{
|
||||
"max_connection_seconds": 86400
|
||||
}
|
||||
```
|
||||
|
||||
**Recommended Settings:**
|
||||
|
||||
- **Production:**
|
||||
- `max_connection_seconds: 86400` (24 hours)
|
||||
|
||||
- **Development:**
|
||||
- `max_connection_seconds: 3600` (1 hour for faster testing)
|
||||
|
||||
- **High-traffic:**
|
||||
- `max_connection_seconds: 43200` (12 hours)
|
||||
|
||||
- **Disabled:**
|
||||
- `max_connection_seconds: 0`
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues arise after deployment:
|
||||
|
||||
1. **Disable connection age limit:**
|
||||
- Set `max_connection_seconds: 0` in config
|
||||
- Restart relay
|
||||
- Monitor for stability
|
||||
|
||||
2. **Revert code changes:**
|
||||
- Remove `check_connection_age()` call from event loop
|
||||
- Remove `cleanup_all_subscriptions_on_startup()` call from main
|
||||
- Restart relay
|
||||
|
||||
3. **Database cleanup (if needed):**
|
||||
- Manually clean up orphaned subscriptions using SQL:
|
||||
```sql
|
||||
UPDATE subscriptions
|
||||
SET ended_at = strftime('%s', 'now')
|
||||
WHERE event_type = 'created' AND ended_at IS NULL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comparison with Original Design
|
||||
|
||||
### Original Design (5 Components)
|
||||
1. Startup cleanup
|
||||
2. Fix WebSocket disconnection logging
|
||||
3. Enhance subscription removal with reason parameter
|
||||
4. Periodic cleanup task (background thread)
|
||||
5. Optimize database VIEW
|
||||
6. Subscription expiration (optional)
|
||||
|
||||
### Simplified Design (2 Components)
|
||||
1. Startup cleanup
|
||||
2. Connection age limit
|
||||
|
||||
### Why Simplified is Better
|
||||
|
||||
**Advantages:**
|
||||
- **Simpler**: 2 components vs 5-6 components
|
||||
- **Faster to implement**: 3-5 hours vs 11-17 hours
|
||||
- **Easier to maintain**: Less code, fewer moving parts
|
||||
- **More reliable**: Fewer potential failure points
|
||||
- **Nostr-aligned**: Leverages client reconnection behavior
|
||||
- **No new threads**: Uses existing event loop
|
||||
- **Universal compatibility**: All clients handle reconnection
|
||||
|
||||
**What We're Not Losing:**
|
||||
- Startup cleanup is identical in both designs
|
||||
- Connection age limit achieves the same goal as periodic cleanup + expiration
|
||||
- Disconnection forces complete cleanup (better than just logging)
|
||||
- Database VIEW optimization not needed if subscriptions don't accumulate
|
||||
|
||||
**Trade-offs:**
|
||||
- Less granular logging (but simpler)
|
||||
- No historical subscription analytics (but cleaner database)
|
||||
- Clients must reconnect periodically (but this is standard Nostr behavior)
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
This simplified design solves the subscription accumulation problem with two pragmatic solutions:
|
||||
|
||||
1. **Startup cleanup** ensures every relay restart starts with a clean slate
|
||||
2. **Connection age limit** prevents long-term accumulation by forcing periodic reconnection
|
||||
|
||||
Both solutions align with Nostr's stateless design where clients are expected to handle reconnection. The implementation is simple, maintainable, and leverages existing code patterns.
|
||||
|
||||
**Key Benefits:**
|
||||
- ✅ Solves the root problem (subscription accumulation)
|
||||
- ✅ Simple to implement (3-5 hours total)
|
||||
- ✅ Easy to maintain (minimal code)
|
||||
- ✅ Universal compatibility (all clients handle reconnection)
|
||||
- ✅ No new threads or background tasks
|
||||
- ✅ Configurable and can be disabled if needed
|
||||
- ✅ Relay restart as emergency fix
|
||||
|
||||
**Next Steps:**
|
||||
1. Implement Component 1 (Startup Cleanup)
|
||||
2. Test thoroughly
|
||||
3. Implement Component 2 (Connection Age Limit)
|
||||
4. Test thoroughly
|
||||
5. Deploy to production
|
||||
6. Monitor CPU usage and subscription counts
|
||||
@@ -188,17 +188,17 @@ update_version_in_header() {
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Update VERSION macro
|
||||
sed -i "s/#define VERSION \".*\"/#define VERSION \"$new_version\"/" src/main.h
|
||||
# Update CRELAY_VERSION macro
|
||||
sed -i "s/#define CRELAY_VERSION \".*\"/#define CRELAY_VERSION \"$new_version\"/" src/main.h
|
||||
|
||||
# Update VERSION_MAJOR macro
|
||||
sed -i "s/#define VERSION_MAJOR [0-9]\+/#define VERSION_MAJOR $major/" src/main.h
|
||||
# Update CRELAY_VERSION_MAJOR macro
|
||||
sed -i "s/#define CRELAY_VERSION_MAJOR [0-9]\+/#define CRELAY_VERSION_MAJOR $major/" src/main.h
|
||||
|
||||
# Update VERSION_MINOR macro
|
||||
sed -i "s/#define VERSION_MINOR .*/#define VERSION_MINOR $minor/" src/main.h
|
||||
# Update CRELAY_VERSION_MINOR macro
|
||||
sed -i "s/#define CRELAY_VERSION_MINOR .*/#define CRELAY_VERSION_MINOR $minor/" src/main.h
|
||||
|
||||
# Update VERSION_PATCH macro
|
||||
sed -i "s/#define VERSION_PATCH [0-9]\+/#define VERSION_PATCH $patch/" src/main.h
|
||||
# Update CRELAY_VERSION_PATCH macro
|
||||
sed -i "s/#define CRELAY_VERSION_PATCH [0-9]\+/#define CRELAY_VERSION_PATCH $patch/" src/main.h
|
||||
|
||||
print_success "Updated version in src/main.h to $new_version"
|
||||
}
|
||||
|
||||
1
nips
1
nips
Submodule nips deleted from 8c45ff5d96
@@ -32,7 +32,7 @@ You're all set up now - just wait for the next crash and then run the coredumpct
|
||||
|
||||
|
||||
--------------------------
|
||||
|
||||
DEBUGGING
|
||||
|
||||
Even simpler: Use this one-liner
|
||||
# Start relay and immediately attach gdb
|
||||
@@ -88,4 +88,5 @@ sudo ufw delete allow 8888/tcp
|
||||
|
||||
lsof -i :7777
|
||||
kill $(lsof -t -i :7777)
|
||||
kill -9 $(lsof -t -i :7777)
|
||||
kill -9 $(lsof -t -i :7777)
|
||||
|
||||
|
||||
342
query_analysis_report.md
Normal file
342
query_analysis_report.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# Database Query Performance Analysis Report
|
||||
|
||||
**Analysis Date:** 2026-02-02
|
||||
**Log Duration:** ~6 minutes (15:24:50 - 15:30:58)
|
||||
**Total Queries:** 366 queries
|
||||
**Data Source:** serverlog.txt
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The relay is experiencing moderate performance issues with an average query time of **10.4ms** and a maximum query time of **672ms**. The primary bottlenecks are:
|
||||
|
||||
1. **Tag-based searches using `json_each()`** - 53% of all queries (194/366)
|
||||
2. **Monitoring system queries** - Taking 540-550ms each
|
||||
3. **Multiple pubkey lookups** - Kind 10002 queries with 15-50 pubkeys
|
||||
|
||||
---
|
||||
|
||||
## Query Performance Metrics
|
||||
|
||||
### Overall Statistics
|
||||
- **Total Queries:** 366
|
||||
- **Average Query Time:** 10,440 μs (10.4 ms)
|
||||
- **Minimum Query Time:** 14 μs
|
||||
- **Maximum Query Time:** 672,846 μs (672.8 ms)
|
||||
- **Slow Queries (>10ms):** 8 queries (2.2%)
|
||||
|
||||
### Query Type Breakdown
|
||||
| Type | Count | Percentage |
|
||||
|------|-------|------------|
|
||||
| REQ | 359 | 98.1% |
|
||||
| MONITOR | 7 | 1.9% |
|
||||
|
||||
---
|
||||
|
||||
## Critical Performance Issues
|
||||
|
||||
### 1. **SLOWEST QUERY: 672ms Tag Search (IP: 192.42.116.178)**
|
||||
|
||||
```sql
|
||||
SELECT id, pubkey, created_at, kind, content, sig, tags
|
||||
FROM events
|
||||
WHERE 1=1
|
||||
AND (kind < 20000 OR kind >= 30000)
|
||||
AND kind IN (5,6300,7000,2004,1622)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM json_each(json(tags))
|
||||
WHERE json_extract(value, '$[0]') = ?
|
||||
AND json_extract(value, '$[1]') IN (?)
|
||||
)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 100
|
||||
```
|
||||
|
||||
**Problem:** Full table scan with JSON parsing for every row
|
||||
**Impact:** 672ms for 0 results (wasted computation)
|
||||
**Root Cause:** No index on tag values, requires scanning all events
|
||||
|
||||
---
|
||||
|
||||
### 2. **Monitoring System Queries: 540-550ms Each**
|
||||
|
||||
```sql
|
||||
SELECT * FROM active_subscriptions_log
|
||||
ORDER BY created_at DESC
|
||||
```
|
||||
|
||||
**Occurrences:** 4 queries in 6 minutes
|
||||
**Average Time:** 545ms
|
||||
**Rows Returned:** 20-52 rows
|
||||
**Problem:** Extremely slow for small result sets
|
||||
**Root Cause:** Likely missing index on `created_at` column
|
||||
|
||||
---
|
||||
|
||||
### 3. **Tag-Based Searches (json_each) - 53% of All Queries**
|
||||
|
||||
- **Total:** 194 queries (53% of all queries)
|
||||
- **Pattern:** `EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE ...)`
|
||||
- **Most Common:** Kind 1984 (105 queries), Kind 1111 (47 queries)
|
||||
- **Problem:** Every tag search requires full JSON parsing
|
||||
|
||||
**Example Pattern:**
|
||||
```sql
|
||||
WHERE kind IN (1984)
|
||||
AND pubkey IN (?)
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM json_each(json(tags))
|
||||
WHERE json_extract(value, '$[0]') = ?
|
||||
AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. **Multiple Pubkey Lookups (Kind 10002)**
|
||||
|
||||
- **Total:** 64 queries for kind 10002
|
||||
- **Average Time:** 2,500-3,300 μs (2.5-3.3ms)
|
||||
- **Pattern:** Queries with 15-50 pubkeys in `IN` clause
|
||||
- **Problem:** Large `IN` clauses without proper indexing
|
||||
|
||||
**Example:**
|
||||
```sql
|
||||
WHERE kind IN (10002)
|
||||
AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Client Activity Analysis
|
||||
|
||||
### Top Query Sources (by IP)
|
||||
| IP Address | Query Count | Percentage | Notes |
|
||||
|------------|-------------|------------|-------|
|
||||
| 45.84.107.222 | 101 | 27.6% | **Highest activity** |
|
||||
| 23.234.109.54 | 69 | 18.9% | Second highest |
|
||||
| 185.220.101.38 | 56 | 15.3% | Third highest |
|
||||
| 192.42.116.178 | 51 | 13.9% | **Source of 672ms query** |
|
||||
| 149.22.80.85 | 34 | 9.3% | |
|
||||
| 174.138.53.241 | 24 | 6.6% | |
|
||||
| Others | 31 | 8.5% | 6 other IPs |
|
||||
|
||||
**Observation:** Top 3 IPs account for 61.8% of all queries
|
||||
|
||||
---
|
||||
|
||||
## Most Common Query Patterns
|
||||
|
||||
| Kind Filter | Query Count | Percentage |
|
||||
|-------------|-------------|------------|
|
||||
| kind IN (1984) | 105 | 28.7% |
|
||||
| kind IN (10002) | 64 | 17.5% |
|
||||
| kind IN (1111) | 47 | 12.8% |
|
||||
| kind IN (0,2,3,10002) | 24 | 6.6% |
|
||||
| kind IN (9735) | 23 | 6.3% |
|
||||
| kind IN (0,30315,10002,10050) | 20 | 5.5% |
|
||||
| Others | 83 | 22.7% |
|
||||
|
||||
---
|
||||
|
||||
## Optimization Recommendations
|
||||
|
||||
### Priority 1: Critical (Immediate Action Required)
|
||||
|
||||
#### 1.1 Add Index on `active_subscriptions_log.created_at`
|
||||
**Impact:** Will reduce monitoring queries from 540ms to <10ms
|
||||
**Effort:** Low
|
||||
**SQL:**
|
||||
```sql
|
||||
CREATE INDEX IF NOT EXISTS idx_active_subscriptions_created_at
|
||||
ON active_subscriptions_log(created_at DESC);
|
||||
```
|
||||
|
||||
#### 1.2 Implement Tag Indexing System
|
||||
**Impact:** Will reduce tag searches from 100-600ms to <10ms
|
||||
**Effort:** High
|
||||
**Options:**
|
||||
- **Option A:** Create separate `event_tags` table with indexes
|
||||
```sql
|
||||
CREATE TABLE event_tags (
|
||||
event_id TEXT NOT NULL,
|
||||
tag_name TEXT NOT NULL,
|
||||
tag_value TEXT NOT NULL,
|
||||
FOREIGN KEY (event_id) REFERENCES events(id)
|
||||
);
|
||||
CREATE INDEX idx_event_tags_lookup ON event_tags(tag_name, tag_value);
|
||||
```
|
||||
- **Option B:** Use SQLite JSON1 extension with generated columns (if available)
|
||||
- **Option C:** Implement application-level tag caching
|
||||
|
||||
**Recommended:** Option A (most reliable and performant)
|
||||
|
||||
---
|
||||
|
||||
### Priority 2: High (Implement Within Week)
|
||||
|
||||
#### 2.1 Optimize Multiple Pubkey Queries
|
||||
**Current:** `pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`
|
||||
**Problem:** Large IN clauses are inefficient
|
||||
**Solution:**
|
||||
- Add composite index: `CREATE INDEX idx_events_kind_pubkey ON events(kind, pubkey, created_at DESC);`
|
||||
- Consider query rewriting for >10 pubkeys
|
||||
|
||||
#### 2.2 Add Query Result Caching
|
||||
**Target Queries:**
|
||||
- Kind 0 (profile) lookups - frequently repeated
|
||||
- Kind 10002 (relay lists) - relatively static
|
||||
- Kind 1984 (reports) - can be cached for 30-60 seconds
|
||||
|
||||
**Implementation:**
|
||||
- Use in-memory LRU cache (e.g., 1000 entries)
|
||||
- Cache key: hash of SQL + parameters
|
||||
- TTL: 30-60 seconds for most queries
|
||||
|
||||
---
|
||||
|
||||
### Priority 3: Medium (Optimize Over Time)
|
||||
|
||||
#### 3.1 Disable or Throttle Monitoring Queries
|
||||
**Current:** Monitoring queries run every ~60 seconds
|
||||
**Impact:** Each query takes 540ms
|
||||
**Options:**
|
||||
- Increase throttle to 300 seconds (5 minutes)
|
||||
- Disable monitoring entirely if not actively used
|
||||
- Optimize `active_subscriptions_log` table structure
|
||||
|
||||
#### 3.2 Implement Query Complexity Limits
|
||||
**Problem:** Some queries scan entire table (e.g., 672ms query returned 0 results)
|
||||
**Solution:**
|
||||
- Reject queries without time bounds (require `since` or `until`)
|
||||
- Limit number of kinds in single query (max 10)
|
||||
- Limit number of pubkeys in single query (max 20)
|
||||
|
||||
#### 3.3 Add Composite Indexes for Common Patterns
|
||||
```sql
|
||||
-- For kind + created_at queries (most common pattern)
|
||||
CREATE INDEX idx_events_kind_created ON events(kind, created_at DESC);
|
||||
|
||||
-- For kind + pubkey + created_at queries
|
||||
CREATE INDEX idx_events_kind_pubkey_created ON events(kind, pubkey, created_at DESC);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Database Schema Recommendations
|
||||
|
||||
### Current Issues
|
||||
1. **No tag indexing** - Forces full table scans with JSON parsing
|
||||
2. **Missing created_at indexes** - Monitoring queries are extremely slow
|
||||
3. **No composite indexes** - Multi-condition queries inefficient
|
||||
|
||||
### Recommended Schema Changes
|
||||
|
||||
```sql
|
||||
-- 1. Add tag indexing table
|
||||
CREATE TABLE IF NOT EXISTS event_tags (
|
||||
event_id TEXT NOT NULL,
|
||||
tag_name TEXT NOT NULL,
|
||||
tag_value TEXT NOT NULL,
|
||||
FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_event_tags_lookup ON event_tags(tag_name, tag_value);
|
||||
CREATE INDEX idx_event_tags_event_id ON event_tags(event_id);
|
||||
|
||||
-- 2. Add monitoring table index
|
||||
CREATE INDEX IF NOT EXISTS idx_active_subscriptions_created_at
|
||||
ON active_subscriptions_log(created_at DESC);
|
||||
|
||||
-- 3. Add composite indexes for common query patterns
|
||||
CREATE INDEX IF NOT EXISTS idx_events_kind_created
|
||||
ON events(kind, created_at DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_events_kind_pubkey_created
|
||||
ON events(kind, pubkey, created_at DESC);
|
||||
|
||||
-- 4. Add index for pubkey lookups
|
||||
CREATE INDEX IF NOT EXISTS idx_events_pubkey_created
|
||||
ON events(pubkey, created_at DESC);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Expected Performance Improvements
|
||||
|
||||
| Optimization | Current Avg | Expected Avg | Improvement |
|
||||
|--------------|-------------|--------------|-------------|
|
||||
| Tag searches (with event_tags table) | 100-600ms | 5-20ms | **95-97%** |
|
||||
| Monitoring queries (with index) | 540ms | 5-10ms | **98%** |
|
||||
| Multiple pubkey queries (with index) | 2.5-3.3ms | 0.5-1ms | **70-80%** |
|
||||
| Overall average query time | 10.4ms | 2-3ms | **70-80%** |
|
||||
|
||||
---
|
||||
|
||||
## Client Behavior Analysis
|
||||
|
||||
### Potentially Abusive Patterns
|
||||
|
||||
#### IP: 192.42.116.178 (51 queries)
|
||||
- **Issue:** Generated the slowest query (672ms)
|
||||
- **Pattern:** Complex tag searches with multiple kinds
|
||||
- **Recommendation:** Monitor for repeated expensive queries
|
||||
|
||||
#### IP: 45.84.107.222 (101 queries - 27.6% of total)
|
||||
- **Issue:** Highest query volume
|
||||
- **Pattern:** Mix of kind 10002, 1984, and various other kinds
|
||||
- **Recommendation:** Acceptable if queries are efficient; monitor for abuse
|
||||
|
||||
### Normal Behavior
|
||||
- Most queries are <1ms (fast)
|
||||
- Majority return 0-10 rows (reasonable)
|
||||
- Query patterns match typical Nostr client behavior
|
||||
|
||||
---
|
||||
|
||||
## Action Plan
|
||||
|
||||
### Immediate (Today)
|
||||
1. ✅ Add index on `active_subscriptions_log.created_at`
|
||||
2. ✅ Increase monitoring throttle from 60s to 300s (or disable)
|
||||
3. ✅ Monitor IP 192.42.116.178 for repeated expensive queries
|
||||
|
||||
### This Week
|
||||
1. ⏳ Design and implement `event_tags` table
|
||||
2. ⏳ Add composite indexes for common query patterns
|
||||
3. ⏳ Implement query complexity limits (require time bounds)
|
||||
|
||||
### This Month
|
||||
1. ⏳ Implement query result caching (LRU cache)
|
||||
2. ⏳ Add query cost estimation and rejection
|
||||
3. ⏳ Optimize subscription matching algorithm
|
||||
|
||||
---
|
||||
|
||||
## Monitoring Recommendations
|
||||
|
||||
### Key Metrics to Track
|
||||
1. **Average query time** - Target: <5ms
|
||||
2. **P95 query time** - Target: <50ms
|
||||
3. **P99 query time** - Target: <100ms
|
||||
4. **Queries >100ms** - Target: <1% of queries
|
||||
5. **Tag search percentage** - Target: <30% after optimization
|
||||
|
||||
### Alert Thresholds
|
||||
- **Critical:** Average query time >20ms for 5 minutes
|
||||
- **Warning:** Any single query >1000ms (1 second)
|
||||
- **Info:** Client making >100 queries/minute
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
The relay is experiencing performance issues primarily due to:
|
||||
1. **Lack of tag indexing** (53% of queries affected)
|
||||
2. **Missing indexes on monitoring tables** (540ms queries)
|
||||
3. **Inefficient multiple pubkey lookups**
|
||||
|
||||
Implementing the recommended optimizations will reduce average query time from **10.4ms to 2-3ms** (70-80% improvement) and eliminate the 500-600ms slow queries entirely.
|
||||
|
||||
**Priority:** Implement tag indexing system and add missing indexes within 1 week to prevent performance degradation as database grows.
|
||||
414
serverlog.txt
Normal file
414
serverlog.txt
Normal file
@@ -0,0 +1,414 @@
|
||||
Feb 02 15:24:50 VM410 c_relay[2374924]: [2026-02-02 15:24:50] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=118us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1769824535 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:24:57 VM410 c_relay[2374924]: [2026-02-02 15:24:57] [QUERY] type=REQ sub=0a30f4b8-ce11-4696-ae08-c5fdb4b8d4a5 ip=162.230.33.62 time=1273us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 1
|
||||
Feb 02 15:24:58 VM410 c_relay[2374924]: [2026-02-02 15:24:58] [QUERY] type=REQ sub=722402c9-2600-4cea-8e9f-1392d02ab6d8 ip=45.56.75.30 time=1260us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 1
|
||||
Feb 02 15:25:04 VM410 c_relay[2374924]: [2026-02-02 15:25:04] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=113us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) AND created_at >= 1770045891 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=OJnSS2 ip=45.84.107.222 time=667us rows=22 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (20,34235,34236,21,22) AND created_at >= 1769441105 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=OJnSS2 ip=45.84.107.222 time=147us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1063,1065) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1769441105 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=111us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (40,41) AND created_at >= 1690242548 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=197us rows=9 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (42) AND created_at >= 1690242548 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=99us rows=4 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (34550) AND created_at >= 1714215225 ORDER BY created_at DESC LIMIT 200
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=2002us rows=100 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (4550) AND created_at >= 1714215225 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=cPd8gE ip=45.84.107.222 time=2472us rows=23 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (39089) ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=cPd8gE ip=45.84.107.222 time=265us rows=9 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30311) AND created_at >= 1769441105 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=cPd8gE ip=45.84.107.222 time=26us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311) AND created_at >= 1769959505 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=hFHFQP ip=45.84.107.222 time=296us rows=10 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,6,16,30402,30023,9802,30818,6969,30296) AND pubkey IN (?) AND created_at >= 1767921282 ORDER BY created_at DESC LIMIT 10
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=hFHFQP ip=45.84.107.222 time=76us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311,1111,30311,23333,1222,1244) AND pubkey IN (?) AND created_at >= 1769999542 ORDER BY created_at DESC LIMIT 10
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=mqavYM ip=45.84.107.222 time=194us rows=7 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30023) AND created_at >= 1769914003 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=mqavYM ip=45.84.107.222 time=328us rows=15 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30402) AND created_at >= 1767453905 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=mqavYM ip=45.84.107.222 time=105us rows=4 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (31990) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=y2laVp ip=45.84.107.222 time=45968us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,7,6,16,9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769441103 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [SLOW_QUERY] 45ms: SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,7,6,16,9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769441103 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=y2laVp ip=45.84.107.222 time=223us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984,23195,42,23333,8,6969,24) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769441103 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=y2laVp ip=45.84.107.222 time=331us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1622,1621,1617,9802,1111,31922,31923,31925,30296,30297) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769441103 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:06 VM410 c_relay[2374924]: [2026-02-02 15:25:06] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=154us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:08 VM410 c_relay[2374924]: [2026-02-02 15:25:08] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=140us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (40,41) AND created_at >= 1661333723 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:08 VM410 c_relay[2374924]: [2026-02-02 15:25:08] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=190us rows=9 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (42) AND created_at >= 1661333723 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:08 VM410 c_relay[2374924]: [2026-02-02 15:25:08] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=87us rows=4 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (34550) AND created_at >= 1690314055 ORDER BY created_at DESC LIMIT 200
|
||||
Feb 02 15:25:08 VM410 c_relay[2374924]: [2026-02-02 15:25:08] [QUERY] type=REQ sub=UCVLmf ip=45.84.107.222 time=1889us rows=100 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (4550) AND created_at >= 1690314055 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:10 VM410 c_relay[2374924]: [2026-02-02 15:25:10] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:25:10 VM410 c_relay[2374924]: [2026-02-02 15:25:10] [WARN ] Event already exists in database
|
||||
Feb 02 15:25:10 VM410 c_relay[2374924]: [2026-02-02 15:25:10] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2647us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:11 VM410 c_relay[2374924]: [2026-02-02 15:25:11] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=115us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:11 VM410 c_relay[2374924]: [2026-02-02 15:25:11] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2809us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:12 VM410 c_relay[2374924]: [2026-02-02 15:25:12] [QUERY] type=REQ sub=hFHFQP ip=45.84.107.222 time=168us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,6,16,30402,30023,9802,30818,6969,30296) AND pubkey IN (?,?,?,?,?,?) AND created_at >= 1770045910 ORDER BY created_at DESC LIMIT 60
|
||||
Feb 02 15:25:12 VM410 c_relay[2374924]: [2026-02-02 15:25:12] [QUERY] type=REQ sub=hFHFQP ip=45.84.107.222 time=119us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311,1111,30311,23333,1222,1244) AND pubkey IN (?,?,?,?,?,?) AND created_at >= 1770045910 ORDER BY created_at DESC LIMIT 60
|
||||
Feb 02 15:25:13 VM410 c_relay[2374924]: [2026-02-02 15:25:13] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2806us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:13 VM410 c_relay[2374924]: [2026-02-02 15:25:13] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2856us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:14 VM410 c_relay[2374924]: [2026-02-02 15:25:14] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2780us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:15 VM410 c_relay[2374924]: [2026-02-02 15:25:15] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2703us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:16 VM410 c_relay[2374924]: [2026-02-02 15:25:16] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=66us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) AND created_at >= 1770045911 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:16 VM410 c_relay[2374924]: [2026-02-02 15:25:16] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2825us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:17 VM410 c_relay[2374924]: [2026-02-02 15:25:17] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=3319us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:19 VM410 c_relay[2374924]: [2026-02-02 15:25:19] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=77us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770045911 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:19 VM410 c_relay[2374924]: [2026-02-02 15:25:19] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2556us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:19 VM410 c_relay[2374924]: [2026-02-02 15:25:19] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=72us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045911 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:19 VM410 c_relay[2374924]: [2026-02-02 15:25:19] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2618us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:19 VM410 c_relay[2374924]: [2026-02-02 15:25:19] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=109us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:19 VM410 c_relay[2374924]: [2026-02-02 15:25:19] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=56us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045919 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:20 VM410 c_relay[2374924]: [2026-02-02 15:25:20] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=63us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045919 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:21 VM410 c_relay[2374924]: [2026-02-02 15:25:21] [QUERY] type=MONITOR sub=event_kinds ip=N/A time=4010us rows=85 sql=SELECT kind, COUNT(*) as count FROM events GROUP BY kind ORDER BY count DESC
|
||||
Feb 02 15:25:21 VM410 c_relay[2374924]: [2026-02-02 15:25:21] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=66us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770045919 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:21 VM410 c_relay[2374924]: [2026-02-02 15:25:21] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2572us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:21 VM410 c_relay[2374924]: [2026-02-02 15:25:21] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=80us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770045920 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:21 VM410 c_relay[2374924]: [2026-02-02 15:25:21] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2734us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:22 VM410 c_relay[2374924]: [2026-02-02 15:25:22] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2520us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:24 VM410 c_relay[2374924]: [2026-02-02 15:25:24] [QUERY] type=MONITOR sub=subscription_details ip=N/A time=547341us rows=35 sql=SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:25:24 VM410 c_relay[2374924]: [2026-02-02 15:25:24] [SLOW_QUERY] 547ms: SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:25:24 VM410 c_relay[2374924]: [2026-02-02 15:25:24] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2524us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:24 VM410 c_relay[2374924]: [2026-02-02 15:25:24] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2708us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:24 VM410 c_relay[2374924]: [2026-02-02 15:25:24] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=133us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045922 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:24 VM410 c_relay[2374924]: [2026-02-02 15:25:24] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=3743us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:25 VM410 c_relay[2374924]: [2026-02-02 15:25:25] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2520us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:26 VM410 c_relay[2374924]: [2026-02-02 15:25:26] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2496us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:26 VM410 c_relay[2374924]: [2026-02-02 15:25:26] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=3300us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:28 VM410 c_relay[2374924]: [2026-02-02 15:25:28] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=118us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045891 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:29 VM410 c_relay[2374924]: [2026-02-02 15:25:29] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2584us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:30 VM410 c_relay[2374924]: [2026-02-02 15:25:30] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=142us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:30 VM410 c_relay[2374924]: [2026-02-02 15:25:30] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=116us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1769821388 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:30 VM410 c_relay[2374924]: [2026-02-02 15:25:30] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2475us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:30 VM410 c_relay[2374924]: [2026-02-02 15:25:30] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=80us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:30 VM410 c_relay[2374924]: [2026-02-02 15:25:30] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=60us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) AND created_at >= 1770045924 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:31 VM410 c_relay[2374924]: [2026-02-02 15:25:31] [QUERY] type=REQ sub=8KXkRK ip=149.22.80.85 time=774us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:31 VM410 c_relay[2374924]: [2026-02-02 15:25:31] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=69us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045931 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:32 VM410 c_relay[2374924]: [2026-02-02 15:25:32] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=121us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:32 VM410 c_relay[2374924]: [2026-02-02 15:25:32] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=77us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1769829216 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:32 VM410 c_relay[2374924]: [2026-02-02 15:25:32] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=48us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:32 VM410 c_relay[2374924]: [2026-02-02 15:25:32] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=55us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770045931 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:33 VM410 c_relay[2374924]: [2026-02-02 15:25:33] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=86us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:33 VM410 c_relay[2374924]: [2026-02-02 15:25:33] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=50us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1769985174 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:33 VM410 c_relay[2374924]: [2026-02-02 15:25:33] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=96us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:33 VM410 c_relay[2374924]: [2026-02-02 15:25:33] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=55us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1769985174 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:34 VM410 c_relay[2374924]: [2026-02-02 15:25:34] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=84us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:34 VM410 c_relay[2374924]: [2026-02-02 15:25:34] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=70us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045934 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:35 VM410 c_relay[2374924]: [2026-02-02 15:25:35] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=72us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1769995802 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:36 VM410 c_relay[2374924]: [2026-02-02 15:25:36] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2602us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:36 VM410 c_relay[2374924]: [2026-02-02 15:25:36] [QUERY] type=REQ sub=9UTHfl ip=45.84.107.222 time=2681us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:39 VM410 c_relay[2374924]: [2026-02-02 15:25:39] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=81us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770045936 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=77us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045932 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=ogRlLS ip=45.88.186.89 time=827us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=82us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045931 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=Ong5MN ip=185.220.101.38 time=140us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (40,41) AND created_at >= 1770045418 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=Ong5MN ip=185.220.101.38 time=28us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (42) AND created_at >= 1770045418 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=Ong5MN ip=185.220.101.38 time=21us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (34550) AND created_at >= 1770045418 ORDER BY created_at DESC LIMIT 200
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=Ong5MN ip=185.220.101.38 time=18us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (4550) AND created_at >= 1770045418 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=WPbjzb ip=185.220.101.38 time=22us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (39089) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=WPbjzb ip=185.220.101.38 time=22us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30311) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=WPbjzb ip=185.220.101.38 time=15us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=f7d8wu ip=185.220.101.38 time=223us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,7,6,16,9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=f7d8wu ip=185.220.101.38 time=154us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984,23195,42,23333,8,6969,24) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=f7d8wu ip=185.220.101.38 time=143us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1622,1621,1617,9802,1111,31922,31923,31925,30296,30297) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=iPpzPF ip=185.220.101.38 time=115us rows=3 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30023) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=iPpzPF ip=185.220.101.38 time=22us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30402) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=iPpzPF ip=185.220.101.38 time=44us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (31990) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=p6rCAU ip=185.220.101.38 time=109us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (20,34235,34236,21,22) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=p6rCAU ip=185.220.101.38 time=137us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1063,1065) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=tdwn4I ip=185.220.101.38 time=803us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 1
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=whcepl ip=185.220.101.38 time=94us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=zjLtJo ip=185.220.101.38 time=168us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,6,16,30402,30023,9802,30818,6969,30296) AND pubkey IN (?,?,?,?,?,?,?,?,?) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 90
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=zjLtJo ip=185.220.101.38 time=130us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311,1111,30311,23333,1222,1244) AND pubkey IN (?,?,?,?,?,?,?,?,?) AND created_at >= 1770045424 ORDER BY created_at DESC LIMIT 90
|
||||
Feb 02 15:25:40 VM410 c_relay[2374924]: [2026-02-02 15:25:40] [QUERY] type=REQ sub=oIETF1 ip=45.88.186.89 time=3137us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:41 VM410 c_relay[2374924]: [2026-02-02 15:25:41] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=90us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770045940 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:41 VM410 c_relay[2374924]: [2026-02-02 15:25:41] [QUERY] type=REQ sub=oIETF1 ip=45.88.186.89 time=2531us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:44 VM410 c_relay[2374924]: [2026-02-02 15:25:44] [QUERY] type=REQ sub=oIETF1 ip=45.88.186.89 time=2414us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:44 VM410 c_relay[2374924]: [2026-02-02 15:25:44] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:25:44 VM410 c_relay[2374924]: [2026-02-02 15:25:44] [WARN ] Event already exists in database
|
||||
Feb 02 15:25:45 VM410 c_relay[2374924]: [2026-02-02 15:25:45] [QUERY] type=REQ sub=oIETF1 ip=45.88.186.89 time=2504us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:48 VM410 c_relay[2374924]: [2026-02-02 15:25:48] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2723us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:49 VM410 c_relay[2374924]: [2026-02-02 15:25:49] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2720us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:49 VM410 c_relay[2374924]: [2026-02-02 15:25:49] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2729us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:50 VM410 c_relay[2374924]: [2026-02-02 15:25:50] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2986us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:50 VM410 c_relay[2374924]: [2026-02-02 15:25:50] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=104us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045941 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:52 VM410 c_relay[2374924]: [2026-02-02 15:25:52] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=3320us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:52 VM410 c_relay[2374924]: [2026-02-02 15:25:52] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2709us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:53 VM410 c_relay[2374924]: [2026-02-02 15:25:53] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2826us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:53 VM410 c_relay[2374924]: [2026-02-02 15:25:53] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2772us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:54 VM410 c_relay[2374924]: [2026-02-02 15:25:54] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2714us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:54 VM410 c_relay[2374924]: [2026-02-02 15:25:54] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2579us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:56 VM410 c_relay[2374924]: [2026-02-02 15:25:56] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2680us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=35GY9M ip=192.42.116.178 time=138us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (20,34235,34236,21,22) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=35GY9M ip=192.42.116.178 time=147us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1063,1065) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=Uwi18b ip=192.42.116.178 time=25us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (39089) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=Uwi18b ip=192.42.116.178 time=69us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30311) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=Uwi18b ip=192.42.116.178 time=22us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ZX97HA ip=192.42.116.178 time=110us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (40,41) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ZX97HA ip=192.42.116.178 time=21us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (42) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 50
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ZX97HA ip=192.42.116.178 time=15us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (34550) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 200
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ZX97HA ip=192.42.116.178 time=14us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (4550) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=101us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=66us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1769982870 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=746us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2530us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ns1zmz ip=192.42.116.178 time=271us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,6,16,30402,30023,9802,30818,6969,30296) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ns1zmz ip=192.42.116.178 time=218us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1311,1111,30311,23333,1222,1244) AND pubkey IN (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ns1zmz ip=192.42.116.178 time=262us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,6,16,30023,30402,9802,1808,30296,1111,30818,1222,1244) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=ns1zmz ip=192.42.116.178 time=79us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=qT9b8u ip=192.42.116.178 time=180us rows=6 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30023) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=qT9b8u ip=192.42.116.178 time=22us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (30402) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=qT9b8u ip=192.42.116.178 time=67us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (31990) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 30
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=rLFWrB ip=192.42.116.178 time=162us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,7,6,16,9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=rLFWrB ip=192.42.116.178 time=126us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984,23195,42,23333,8,6969,24) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=rLFWrB ip=192.42.116.178 time=114us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1622,1621,1617,9802,1111,31922,31923,31925,30296,30297) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1770044415 ORDER BY created_at DESC LIMIT 20
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=147us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:57 VM410 c_relay[2374924]: [2026-02-02 15:25:57] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=60us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) AND created_at >= 1770045950 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:58 VM410 c_relay[2374924]: [2026-02-02 15:25:58] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=3383us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:59 VM410 c_relay[2374924]: [2026-02-02 15:25:59] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=83us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) AND created_at >= 1770045921 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:59 VM410 c_relay[2374924]: [2026-02-02 15:25:59] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2627us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:59 VM410 c_relay[2374924]: [2026-02-02 15:25:59] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=87us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770045921 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:25:59 VM410 c_relay[2374924]: [2026-02-02 15:25:59] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2753us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:00 VM410 c_relay[2374924]: [2026-02-02 15:26:00] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=184us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:00 VM410 c_relay[2374924]: [2026-02-02 15:26:00] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=102us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045924 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:00 VM410 c_relay[2374924]: [2026-02-02 15:26:00] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2534us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:00 VM410 c_relay[2374924]: [2026-02-02 15:26:00] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=91us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) AND created_at >= 1769982870 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:00 VM410 c_relay[2374924]: [2026-02-02 15:26:00] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=169us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:00 VM410 c_relay[2374924]: [2026-02-02 15:26:00] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=101us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045924 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:01 VM410 c_relay[2374924]: [2026-02-02 15:26:01] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=83us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045960 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:01 VM410 c_relay[2374924]: [2026-02-02 15:26:01] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2516us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:02 VM410 c_relay[2374924]: [2026-02-02 15:26:02] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=89us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770045960 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:02 VM410 c_relay[2374924]: [2026-02-02 15:26:02] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2672us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:02 VM410 c_relay[2374924]: [2026-02-02 15:26:02] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=102us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045962 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:02 VM410 c_relay[2374924]: [2026-02-02 15:26:02] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2693us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:03 VM410 c_relay[2374924]: [2026-02-02 15:26:03] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2541us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:03 VM410 c_relay[2374924]: [2026-02-02 15:26:03] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=129us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:03 VM410 c_relay[2374924]: [2026-02-02 15:26:03] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=76us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045962 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:05 VM410 c_relay[2374924]: [2026-02-02 15:26:05] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=90us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045964 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:05 VM410 c_relay[2374924]: [2026-02-02 15:26:05] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2554us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:05 VM410 c_relay[2374924]: [2026-02-02 15:26:05] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=88us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770045964 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:05 VM410 c_relay[2374924]: [2026-02-02 15:26:05] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2662us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:05 VM410 c_relay[2374924]: [2026-02-02 15:26:05] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=84us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770045964 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:05 VM410 c_relay[2374924]: [2026-02-02 15:26:05] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2760us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:06 VM410 c_relay[2374924]: [2026-02-02 15:26:06] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=89us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) AND created_at >= 1770045966 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:06 VM410 c_relay[2374924]: [2026-02-02 15:26:06] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2851us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=360us rows=2 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=LDheBy ip=192.42.116.178 time=129us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=103us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=73us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) AND created_at >= 1770037383 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2774us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?,?,?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2840us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=108us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:07 VM410 c_relay[2374924]: [2026-02-02 15:26:07] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=71us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) AND created_at >= 1770037383 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:08 VM410 c_relay[2374924]: [2026-02-02 15:26:08] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2772us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:09 VM410 c_relay[2374924]: [2026-02-02 15:26:09] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2898us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:09 VM410 c_relay[2374924]: [2026-02-02 15:26:09] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=102us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045967 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:09 VM410 c_relay[2374924]: [2026-02-02 15:26:09] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2532us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:09 VM410 c_relay[2374924]: [2026-02-02 15:26:09] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=94us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) AND created_at >= 1770045966 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:10 VM410 c_relay[2374924]: [2026-02-02 15:26:10] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2515us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:10 VM410 c_relay[2374924]: [2026-02-02 15:26:10] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=118us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:10 VM410 c_relay[2374924]: [2026-02-02 15:26:10] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=71us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) AND created_at >= 1770045970 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:11 VM410 c_relay[2374924]: [2026-02-02 15:26:11] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2570us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:13 VM410 c_relay[2374924]: [2026-02-02 15:26:13] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=81us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770045970 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:13 VM410 c_relay[2374924]: [2026-02-02 15:26:13] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2406us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:15 VM410 c_relay[2374924]: [2026-02-02 15:26:15] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=88us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) AND created_at >= 1770045971 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:15 VM410 c_relay[2374924]: [2026-02-02 15:26:15] [QUERY] type=REQ sub=tUYXZd ip=45.84.107.222 time=775us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:15 VM410 c_relay[2374924]: [2026-02-02 15:26:15] [QUERY] type=REQ sub=LDheBy ip=192.42.116.178 time=120us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:15 VM410 c_relay[2374924]: [2026-02-02 15:26:15] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=120us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:15 VM410 c_relay[2374924]: [2026-02-02 15:26:15] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=89us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770045962 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:15 VM410 c_relay[2374924]: [2026-02-02 15:26:15] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2625us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:16 VM410 c_relay[2374924]: [2026-02-02 15:26:16] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=361us rows=2 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:16 VM410 c_relay[2374924]: [2026-02-02 15:26:16] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=90us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045971 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:16 VM410 c_relay[2374924]: [2026-02-02 15:26:16] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2492us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:16 VM410 c_relay[2374924]: [2026-02-02 15:26:16] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2503us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:16 VM410 c_relay[2374924]: [2026-02-02 15:26:16] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=98us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045975 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:17 VM410 c_relay[2374924]: [2026-02-02 15:26:17] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=3386us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:18 VM410 c_relay[2374924]: [2026-02-02 15:26:18] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=94us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045965 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:18 VM410 c_relay[2374924]: [2026-02-02 15:26:18] [QUERY] type=REQ sub=OOZawU ip=185.220.101.38 time=2478us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:18 VM410 c_relay[2374924]: [2026-02-02 15:26:18] [QUERY] type=REQ sub=4HGpE1 ip=192.42.116.178 time=390940us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,7,6,16,1984,9735,6969,1040,1010,1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 1000
|
||||
Feb 02 15:26:18 VM410 c_relay[2374924]: [2026-02-02 15:26:18] [SLOW_QUERY] 390ms: SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,7,6,16,1984,9735,6969,1040,1010,1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 1000
|
||||
Feb 02 15:26:19 VM410 c_relay[2374924]: [2026-02-02 15:26:19] [QUERY] type=REQ sub=4HGpE1 ip=192.42.116.178 time=672846us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (5,6300,7000,2004,1622) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:26:19 VM410 c_relay[2374924]: [2026-02-02 15:26:19] [SLOW_QUERY] 672ms: SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (5,6300,7000,2004,1622) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:26:19 VM410 c_relay[2374924]: [2026-02-02 15:26:19] [QUERY] type=REQ sub=4HGpE1 ip=192.42.116.178 time=277019us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 1000
|
||||
Feb 02 15:26:19 VM410 c_relay[2374924]: [2026-02-02 15:26:19] [SLOW_QUERY] 277ms: SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1,1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 1000
|
||||
Feb 02 15:26:20 VM410 c_relay[2374924]: [2026-02-02 15:26:20] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=88us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045924 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:20 VM410 c_relay[2374924]: [2026-02-02 15:26:20] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=80us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045971 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:20 VM410 c_relay[2374924]: [2026-02-02 15:26:20] [QUERY] type=REQ sub=LDheBy ip=192.42.116.178 time=194us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:20 VM410 c_relay[2374924]: [2026-02-02 15:26:20] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=368us rows=2 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:22 VM410 c_relay[2374924]: [2026-02-02 15:26:22] [QUERY] type=MONITOR sub=event_kinds ip=N/A time=5179us rows=85 sql=SELECT kind, COUNT(*) as count FROM events GROUP BY kind ORDER BY count DESC
|
||||
Feb 02 15:26:22 VM410 c_relay[2374924]: [2026-02-02 15:26:22] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=134us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:22 VM410 c_relay[2374924]: [2026-02-02 15:26:22] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=72us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770045922 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:23 VM410 c_relay[2374924]: [2026-02-02 15:26:23] [QUERY] type=MONITOR sub=subscription_details ip=N/A time=542356us rows=52 sql=SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:26:23 VM410 c_relay[2374924]: [2026-02-02 15:26:23] [SLOW_QUERY] 542ms: SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:26:23 VM410 c_relay[2374924]: [2026-02-02 15:26:23] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=87us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045950 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:34 VM410 c_relay[2374924]: [2026-02-02 15:26:34] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=134us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:34 VM410 c_relay[2374924]: [2026-02-02 15:26:34] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=85us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) AND created_at >= 1769993156 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:34 VM410 c_relay[2374924]: [2026-02-02 15:26:34] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=202us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:34 VM410 c_relay[2374924]: [2026-02-02 15:26:34] [QUERY] type=REQ sub=LDheBy ip=192.42.116.178 time=56us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [QUERY] type=REQ sub=8KXkRK ip=149.22.80.85 time=769us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=65us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?)) AND created_at >= 1770045643 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=123us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=80us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045941 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [WARN ] Event already exists in database
|
||||
Feb 02 15:26:35 VM410 c_relay[2374924]: [2026-02-02 15:26:35] [QUERY] type=REQ sub=01KGFF32ARGD9PQ5D964QPR0MW ip=174.138.53.241 time=184us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044637 AND created_at <= 1770044697 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=126us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=82us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1769985337 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=68us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) AND created_at >= 1770045995 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=60us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=3Ikl7m ip=45.84.107.222 time=52us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?)) AND created_at >= 1770045977 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=54us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=52us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1769996479 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=278us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:36 VM410 c_relay[2374924]: [2026-02-02 15:26:36] [QUERY] type=REQ sub=01KGFFCYJC26RZBX5M0BRKRJ7X ip=174.138.53.241 time=130us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044697 AND created_at <= 1770044757 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=81us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045997 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [WARN ] Event already exists in database
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [WARN ] Event already exists in database
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:26:45 VM410 c_relay[2374924]: [2026-02-02 15:26:45] [WARN ] Event already exists in database
|
||||
Feb 02 15:26:46 VM410 c_relay[2374924]: [2026-02-02 15:26:46] [QUERY] type=REQ sub=01KGFFD7XV17M4YWK1WZ27EPH2 ip=174.138.53.241 time=184us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044757 AND created_at <= 1770044817 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:46 VM410 c_relay[2374924]: [2026-02-02 15:26:46] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=77us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045997 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:47 VM410 c_relay[2374924]: [2026-02-02 15:26:47] [QUERY] type=REQ sub=01KGFFD93RFZCTMRF4MDTME2GY ip=174.138.53.241 time=142us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044817 AND created_at <= 1770044877 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:48 VM410 c_relay[2374924]: [2026-02-02 15:26:48] [QUERY] type=REQ sub=01KGFFDABH2QP5W42EPF9HA8MJ ip=174.138.53.241 time=142us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044877 AND created_at <= 1770044937 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:50 VM410 c_relay[2374924]: [2026-02-02 15:26:50] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=95us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770046006 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:51 VM410 c_relay[2374924]: [2026-02-02 15:26:51] [QUERY] type=REQ sub=01KGFFDBKRJED1RTXXS89A01YT ip=174.138.53.241 time=147us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044937 AND created_at <= 1770044997 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:52 VM410 c_relay[2374924]: [2026-02-02 15:26:52] [QUERY] type=REQ sub=01KGFFDDYBSHC6QZM3RT2KTGZ1 ip=174.138.53.241 time=137us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770044997 AND created_at <= 1770045057 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:52 VM410 c_relay[2374924]: [2026-02-02 15:26:52] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=61us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045940 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:53 VM410 c_relay[2374924]: [2026-02-02 15:26:53] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=70us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770045940 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:53 VM410 c_relay[2374924]: [2026-02-02 15:26:53] [QUERY] type=REQ sub=01KGFFDF6903081N4P20GJ6Q9G ip=174.138.53.241 time=150us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045057 AND created_at <= 1770045117 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:55 VM410 c_relay[2374924]: [2026-02-02 15:26:55] [QUERY] type=REQ sub=01KGFFDGEGCY4241AAQ0A1QQ71 ip=174.138.53.241 time=131us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045117 AND created_at <= 1770045177 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:56 VM410 c_relay[2374924]: [2026-02-02 15:26:56] [QUERY] type=REQ sub=01KGFFDHRQ4GWM6P00V1KARSKP ip=174.138.53.241 time=132us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045177 AND created_at <= 1770045237 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:57 VM410 c_relay[2374924]: [2026-02-02 15:26:57] [QUERY] type=REQ sub=01KGFFDJYR90K4C23R2YNDANG8 ip=174.138.53.241 time=132us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045237 AND created_at <= 1770045297 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:26:58 VM410 c_relay[2374924]: [2026-02-02 15:26:58] [QUERY] type=REQ sub=01KGFFDM5BTX2YY7QNNWDVFEA2 ip=174.138.53.241 time=134us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045297 AND created_at <= 1770045357 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:00 VM410 c_relay[2374924]: [2026-02-02 15:27:00] [QUERY] type=REQ sub=01KGFFDND2HJSZ42G5JJRNMCMH ip=174.138.53.241 time=135us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045357 AND created_at <= 1770045417 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:01 VM410 c_relay[2374924]: [2026-02-02 15:27:01] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=82us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770046014 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:01 VM410 c_relay[2374924]: [2026-02-02 15:27:01] [QUERY] type=REQ sub=01KGFFDPKPR6TY8515BNR9WSGS ip=174.138.53.241 time=124us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045417 AND created_at <= 1770045477 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:02 VM410 c_relay[2374924]: [2026-02-02 15:27:02] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=66us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?)) AND created_at >= 1770045940 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:02 VM410 c_relay[2374924]: [2026-02-02 15:27:02] [QUERY] type=REQ sub=01KGFFDQSHRSXKCY86NHH7YSPG ip=174.138.53.241 time=134us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045477 AND created_at <= 1770045537 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:03 VM410 c_relay[2374924]: [2026-02-02 15:27:03] [QUERY] type=REQ sub=QA2Ntv ip=45.84.107.222 time=755us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:03 VM410 c_relay[2374924]: [2026-02-02 15:27:03] [QUERY] type=REQ sub=01KGFFDRZVSAT3PAH5NW63H17M ip=174.138.53.241 time=123us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045537 AND created_at <= 1770045597 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:05 VM410 c_relay[2374924]: [2026-02-02 15:27:05] [QUERY] type=REQ sub=01KGFFDT804TFEK2280KTN7ATX ip=174.138.53.241 time=192us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045597 AND created_at <= 1770045657 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:06 VM410 c_relay[2374924]: [2026-02-02 15:27:06] [QUERY] type=REQ sub=01KGFFDVG7XZ28YWWSB5RXB9AD ip=174.138.53.241 time=204us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045657 AND created_at <= 1770045717 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=43us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=LDheBy ip=192.42.116.178 time=153us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=dOq1Q5 ip=192.42.116.178 time=58us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770045983 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=kdMYZx ip=192.42.116.178 time=443us rows=2 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=QJt6OV8vtYL87OquVRHpO ip=23.234.109.54 time=239us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=QJt6OV8vtYL87OquVRHpO ip=23.234.109.54 time=220us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=_7nKaTl6qiY-QIej4nyIf ip=23.234.109.54 time=685us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=53us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770046012 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:08 VM410 c_relay[2374924]: [2026-02-02 15:27:08] [QUERY] type=REQ sub=8KXkRK ip=149.22.80.85 time=765us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:10 VM410 c_relay[2374924]: [2026-02-02 15:27:10] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=72us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770046012 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:10 VM410 c_relay[2374924]: [2026-02-02 15:27:10] [QUERY] type=REQ sub=01KGFFDZ0323GTS07BCGFK8EXB ip=174.138.53.241 time=139us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045717 AND created_at <= 1770045777 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:11 VM410 c_relay[2374924]: [2026-02-02 15:27:11] [QUERY] type=REQ sub=01KGFFE09F1C3S62PPC1SWZF2N ip=174.138.53.241 time=138us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045777 AND created_at <= 1770045837 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:13 VM410 c_relay[2374924]: [2026-02-02 15:27:13] [QUERY] type=REQ sub=m7MXTEFU-2N8iz6Aw64KN ip=23.234.109.54 time=175us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:13 VM410 c_relay[2374924]: [2026-02-02 15:27:13] [QUERY] type=REQ sub=m7MXTEFU-2N8iz6Aw64KN ip=23.234.109.54 time=126us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:13 VM410 c_relay[2374924]: [2026-02-02 15:27:13] [QUERY] type=REQ sub=rXd-0vN0ZMqOZULEap87J ip=23.234.109.54 time=659us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:15 VM410 c_relay[2374924]: [2026-02-02 15:27:15] [QUERY] type=REQ sub=01KGFFE3WE844R2GJABPMEK318 ip=174.138.53.241 time=133us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045837 AND created_at <= 1770045897 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:16 VM410 c_relay[2374924]: [2026-02-02 15:27:16] [QUERY] type=REQ sub=Iu1fkE ip=149.22.80.85 time=40us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:16 VM410 c_relay[2374924]: [2026-02-02 15:27:16] [QUERY] type=REQ sub=01KGFFE554T4SJJKJ841ZS7W95 ip=174.138.53.241 time=124us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045897 AND created_at <= 1770045957 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:16 VM410 c_relay[2374924]: [2026-02-02 15:27:16] [QUERY] type=REQ sub=8KXkRK ip=149.22.80.85 time=333us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:17 VM410 c_relay[2374924]: [2026-02-02 15:27:17] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=115us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:17 VM410 c_relay[2374924]: [2026-02-02 15:27:17] [QUERY] type=REQ sub=9Aezja ip=149.22.80.85 time=54us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?,?,?,?)) AND created_at >= 1770046031 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:18 VM410 c_relay[2374924]: [2026-02-02 15:27:18] [QUERY] type=REQ sub=8KXkRK ip=149.22.80.85 time=827us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:21 VM410 c_relay[2374924]: [2026-02-02 15:27:21] [QUERY] type=REQ sub=uOcrsTkf_0VCWJWHnCeRs ip=23.234.109.54 time=174us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:21 VM410 c_relay[2374924]: [2026-02-02 15:27:21] [QUERY] type=REQ sub=uOcrsTkf_0VCWJWHnCeRs ip=23.234.109.54 time=126us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:21 VM410 c_relay[2374924]: [2026-02-02 15:27:21] [QUERY] type=REQ sub=u21yCeRMuJB7zZ2amCbtS ip=23.234.109.54 time=524us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:24 VM410 c_relay[2374924]: [2026-02-02 15:27:24] [QUERY] type=MONITOR sub=subscription_details ip=N/A time=549665us rows=40 sql=SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:27:24 VM410 c_relay[2374924]: [2026-02-02 15:27:24] [SLOW_QUERY] 549ms: SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:27:25 VM410 c_relay[2374924]: [2026-02-02 15:27:25] [QUERY] type=REQ sub=WlGa7VSrgAjZbf2OfWf1_ ip=23.234.109.54 time=171us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:25 VM410 c_relay[2374924]: [2026-02-02 15:27:25] [QUERY] type=REQ sub=WlGa7VSrgAjZbf2OfWf1_ ip=23.234.109.54 time=146us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:25 VM410 c_relay[2374924]: [2026-02-02 15:27:25] [QUERY] type=REQ sub=R-_AmAr-PfoMstqY5Be-M ip=23.234.109.54 time=492us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:31 VM410 c_relay[2374924]: [2026-02-02 15:27:31] [QUERY] type=REQ sub=x1gx0bsCihgcNcg4MJBt_ ip=23.234.109.54 time=166us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:31 VM410 c_relay[2374924]: [2026-02-02 15:27:31] [QUERY] type=REQ sub=x1gx0bsCihgcNcg4MJBt_ ip=23.234.109.54 time=131us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:31 VM410 c_relay[2374924]: [2026-02-02 15:27:31] [QUERY] type=REQ sub=p0W093vNjLM7CK1LZhDP- ip=23.234.109.54 time=494us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:31 VM410 c_relay[2374924]: [2026-02-02 15:27:31] [QUERY] type=MONITOR sub=event_kinds ip=N/A time=3904us rows=85 sql=SELECT kind, COUNT(*) as count FROM events GROUP BY kind ORDER BY count DESC
|
||||
Feb 02 15:27:33 VM410 c_relay[2374924]: [2026-02-02 15:27:33] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:27:33 VM410 c_relay[2374924]: [2026-02-02 15:27:33] [WARN ] Event already exists in database
|
||||
Feb 02 15:27:37 VM410 c_relay[2374924]: [2026-02-02 15:27:37] [QUERY] type=REQ sub=ehte7qOjeHmMkZkNRlChh ip=23.234.109.54 time=167us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:37 VM410 c_relay[2374924]: [2026-02-02 15:27:37] [QUERY] type=REQ sub=ehte7qOjeHmMkZkNRlChh ip=23.234.109.54 time=144us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:37 VM410 c_relay[2374924]: [2026-02-02 15:27:37] [QUERY] type=REQ sub=d1upacGWHza3RlLNmcJlN ip=23.234.109.54 time=502us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:38 VM410 c_relay[2374924]: [2026-02-02 15:27:38] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=75us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) AND created_at >= 1770045641 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:39 VM410 c_relay[2374924]: [2026-02-02 15:27:39] [QUERY] type=REQ sub=VOxwOV ip=185.220.101.162 time=126us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:39 VM410 c_relay[2374924]: [2026-02-02 15:27:39] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=62us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770045638 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:40 VM410 c_relay[2374924]: [2026-02-02 15:27:40] [QUERY] type=REQ sub=oGFQzJ ip=185.220.101.162 time=583us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:40 VM410 c_relay[2374924]: [2026-02-02 15:27:40] [QUERY] type=REQ sub=VOxwOV ip=185.220.101.162 time=137us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:40 VM410 c_relay[2374924]: [2026-02-02 15:27:40] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=149us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?)) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:40 VM410 c_relay[2374924]: [2026-02-02 15:27:40] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=76us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?)) AND created_at >= 1770037629 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:40 VM410 c_relay[2374924]: [2026-02-02 15:27:40] [QUERY] type=REQ sub=oGFQzJ ip=185.220.101.162 time=359us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?,?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:40 VM410 c_relay[2374924]: [2026-02-02 15:27:40] [QUERY] type=REQ sub=VOxwOV ip=185.220.101.162 time=49us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:43 VM410 c_relay[2374924]: [2026-02-02 15:27:43] [QUERY] type=REQ sub=oGFQzJ ip=185.220.101.162 time=783us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:43 VM410 c_relay[2374924]: [2026-02-02 15:27:43] [QUERY] type=REQ sub=bYNBAU ip=185.220.101.162 time=55us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1984) AND pubkey IN (?) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?,?,?,?,?,?,?,?)) AND created_at >= 1770046060 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:27:44 VM410 c_relay[2374924]: [2026-02-02 15:27:44] [QUERY] type=REQ sub=J8ZOXPjFm0nTBVsN9b-7w ip=23.234.109.54 time=165us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:44 VM410 c_relay[2374924]: [2026-02-02 15:27:44] [QUERY] type=REQ sub=J8ZOXPjFm0nTBVsN9b-7w ip=23.234.109.54 time=133us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:44 VM410 c_relay[2374924]: [2026-02-02 15:27:44] [QUERY] type=REQ sub=FLnpkLMEeKyuv6mulU9LV ip=23.234.109.54 time=468us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:58 VM410 c_relay[2374924]: [2026-02-02 15:27:58] [QUERY] type=REQ sub=kwq3NrCSQsM16MH0SXfmt ip=23.234.109.54 time=150us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:58 VM410 c_relay[2374924]: [2026-02-02 15:27:58] [QUERY] type=REQ sub=kwq3NrCSQsM16MH0SXfmt ip=23.234.109.54 time=110us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:27:58 VM410 c_relay[2374924]: [2026-02-02 15:27:58] [QUERY] type=REQ sub=cGmEgDlxzNjdkGDA9tDDQ ip=23.234.109.54 time=466us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:01 VM410 c_relay[2374924]: [2026-02-02 15:28:01] [QUERY] type=REQ sub=mlRVkaEwZoIwAcqAGoRlA ip=23.234.109.54 time=147us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:01 VM410 c_relay[2374924]: [2026-02-02 15:28:01] [QUERY] type=REQ sub=mlRVkaEwZoIwAcqAGoRlA ip=23.234.109.54 time=111us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:01 VM410 c_relay[2374924]: [2026-02-02 15:28:01] [QUERY] type=REQ sub=XdiqVm-z62jQfmvQStbRf ip=23.234.109.54 time=446us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:10 VM410 c_relay[2374924]: [2026-02-02 15:28:10] [QUERY] type=REQ sub=VOxwOV ip=185.220.101.162 time=75us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:28:17 VM410 c_relay[2374924]: [2026-02-02 15:28:17] [QUERY] type=REQ sub=Gkv5Iq46m0NzwYE8qIMO5 ip=23.234.109.54 time=168us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:17 VM410 c_relay[2374924]: [2026-02-02 15:28:17] [QUERY] type=REQ sub=Gkv5Iq46m0NzwYE8qIMO5 ip=23.234.109.54 time=127us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:17 VM410 c_relay[2374924]: [2026-02-02 15:28:17] [QUERY] type=REQ sub=p3gN5DLoV7Cpcow4WdYYc ip=23.234.109.54 time=513us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:20 VM410 c_relay[2374924]: [2026-02-02 15:28:20] [QUERY] type=REQ sub=X5ARK-fb4gzK5PvYJLw8g ip=23.234.109.54 time=163us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:20 VM410 c_relay[2374924]: [2026-02-02 15:28:20] [QUERY] type=REQ sub=X5ARK-fb4gzK5PvYJLw8g ip=23.234.109.54 time=124us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:20 VM410 c_relay[2374924]: [2026-02-02 15:28:20] [QUERY] type=REQ sub=sAxhumER9a_bgQL19fDnC ip=23.234.109.54 time=486us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:22 VM410 c_relay[2374924]: [2026-02-02 15:28:22] [QUERY] type=REQ sub=egWA43UcB_hxUMIAayi8X ip=23.234.109.54 time=162us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:22 VM410 c_relay[2374924]: [2026-02-02 15:28:22] [QUERY] type=REQ sub=egWA43UcB_hxUMIAayi8X ip=23.234.109.54 time=150us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:22 VM410 c_relay[2374924]: [2026-02-02 15:28:22] [QUERY] type=REQ sub=VHmguE-_Dccioy96D-UzA ip=23.234.109.54 time=489us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:22 VM410 c_relay[2374924]: [2026-02-02 15:28:22] [QUERY] type=REQ sub=01KGFFFEJJ34MCH6JSVG13Q9A0 ip=174.138.53.241 time=119us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770045957 AND created_at <= 1770046017 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:28:22 VM410 c_relay[2374924]: [2026-02-02 15:28:22] [QUERY] type=REQ sub=oGFQzJ ip=185.220.101.162 time=738us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,30315,10002,10050) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:28:25 VM410 c_relay[2374924]: [2026-02-02 15:28:25] [QUERY] type=MONITOR sub=subscription_details ip=N/A time=545883us rows=20 sql=SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:28:25 VM410 c_relay[2374924]: [2026-02-02 15:28:25] [SLOW_QUERY] 545ms: SELECT * FROM active_subscriptions_log ORDER BY created_at DESC
|
||||
Feb 02 15:28:25 VM410 c_relay[2374924]: [2026-02-02 15:28:25] [QUERY] type=REQ sub=02KcF8i0YQwH61YEkCQVy ip=23.234.109.54 time=159us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:25 VM410 c_relay[2374924]: [2026-02-02 15:28:25] [QUERY] type=REQ sub=02KcF8i0YQwH61YEkCQVy ip=23.234.109.54 time=125us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:25 VM410 c_relay[2374924]: [2026-02-02 15:28:25] [QUERY] type=REQ sub=cEYb9eVQw_pKazQltPHRA ip=23.234.109.54 time=492us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:46 VM410 c_relay[2374924]: [2026-02-02 15:28:46] [QUERY] type=REQ sub=EMHJdfUBcNsG054ULepHB ip=23.234.109.54 time=157us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:46 VM410 c_relay[2374924]: [2026-02-02 15:28:46] [QUERY] type=REQ sub=EMHJdfUBcNsG054ULepHB ip=23.234.109.54 time=115us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:46 VM410 c_relay[2374924]: [2026-02-02 15:28:46] [QUERY] type=REQ sub=aIR5lDZg26zPqCukNrmxG ip=23.234.109.54 time=495us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:49 VM410 c_relay[2374924]: [2026-02-02 15:28:49] [QUERY] type=REQ sub=7lT9hnLoLDVEzW0Ya5tbT ip=23.234.109.54 time=173us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:49 VM410 c_relay[2374924]: [2026-02-02 15:28:49] [QUERY] type=REQ sub=7lT9hnLoLDVEzW0Ya5tbT ip=23.234.109.54 time=113us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:49 VM410 c_relay[2374924]: [2026-02-02 15:28:49] [QUERY] type=REQ sub=ASqP8ytZqzgSnMf-S9mMM ip=23.234.109.54 time=513us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:51 VM410 c_relay[2374924]: [2026-02-02 15:28:51] [QUERY] type=REQ sub=aoG1utZhV8l56hi48MAwv ip=23.234.109.54 time=206us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:51 VM410 c_relay[2374924]: [2026-02-02 15:28:51] [QUERY] type=REQ sub=aoG1utZhV8l56hi48MAwv ip=23.234.109.54 time=159us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:51 VM410 c_relay[2374924]: [2026-02-02 15:28:51] [QUERY] type=REQ sub=evych4y_oHegy7DTnvcoT ip=23.234.109.54 time=503us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:53 VM410 c_relay[2374924]: [2026-02-02 15:28:53] [QUERY] type=REQ sub=r37ORuZaBJpUu1xlGneJQ ip=23.234.109.54 time=145us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:53 VM410 c_relay[2374924]: [2026-02-02 15:28:53] [QUERY] type=REQ sub=r37ORuZaBJpUu1xlGneJQ ip=23.234.109.54 time=109us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:53 VM410 c_relay[2374924]: [2026-02-02 15:28:53] [QUERY] type=REQ sub=X-V_UtQKADSXp-FQk4vfC ip=23.234.109.54 time=515us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:57 VM410 c_relay[2374924]: [2026-02-02 15:28:57] [QUERY] type=REQ sub=0ZQfqsahO_8n6wQIgs7GG ip=23.234.109.54 time=159us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:57 VM410 c_relay[2374924]: [2026-02-02 15:28:57] [QUERY] type=REQ sub=0ZQfqsahO_8n6wQIgs7GG ip=23.234.109.54 time=113us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:57 VM410 c_relay[2374924]: [2026-02-02 15:28:57] [QUERY] type=REQ sub=1KUmNVm4R_MCnn6IbcnaR ip=23.234.109.54 time=510us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:59 VM410 c_relay[2374924]: [2026-02-02 15:28:59] [QUERY] type=REQ sub=gN-D_NZ78hJmpRuNQJFN0 ip=23.234.109.54 time=157us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:59 VM410 c_relay[2374924]: [2026-02-02 15:28:59] [QUERY] type=REQ sub=gN-D_NZ78hJmpRuNQJFN0 ip=23.234.109.54 time=114us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:28:59 VM410 c_relay[2374924]: [2026-02-02 15:28:59] [QUERY] type=REQ sub=bw3Zc0Cqm8v9uhvQ2-wUl ip=23.234.109.54 time=463us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [QUERY] type=REQ sub=zwRytt3tAZayqQJ35KXg1 ip=23.234.109.54 time=152us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [QUERY] type=REQ sub=zwRytt3tAZayqQJ35KXg1 ip=23.234.109.54 time=115us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [QUERY] type=REQ sub=u4D7oxtyg0g1pDCtxgNJT ip=23.234.109.54 time=470us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [QUERY] type=REQ sub=01KGFFH97H8R06PT89DAG2636S ip=174.138.53.241 time=120us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,2,3,10002) AND created_at >= 1770046017 AND created_at <= 1770046077 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:02 VM410 c_relay[2374924]: [2026-02-02 15:29:02] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [ERROR] INSERT failed: rc=19, extended_errcode=1555, msg=UNIQUE constraint failed: events.id
|
||||
Feb 02 15:29:03 VM410 c_relay[2374924]: [2026-02-02 15:29:03] [WARN ] Event already exists in database
|
||||
Feb 02 15:29:07 VM410 c_relay[2374924]: [2026-02-02 15:29:07] [QUERY] type=REQ sub=uC5hBOdRC2cYUW5IoFrRJ ip=23.234.109.54 time=157us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:07 VM410 c_relay[2374924]: [2026-02-02 15:29:07] [QUERY] type=REQ sub=uC5hBOdRC2cYUW5IoFrRJ ip=23.234.109.54 time=137us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:07 VM410 c_relay[2374924]: [2026-02-02 15:29:07] [QUERY] type=REQ sub=0sUX-STXOqhVykuLarYHD ip=23.234.109.54 time=460us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:13 VM410 c_relay[2374924]: [2026-02-02 15:29:13] [QUERY] type=REQ sub=RoAxRwuoW-j9Pj-uunXfR ip=23.234.109.54 time=234us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:13 VM410 c_relay[2374924]: [2026-02-02 15:29:13] [QUERY] type=REQ sub=RoAxRwuoW-j9Pj-uunXfR ip=23.234.109.54 time=115us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:13 VM410 c_relay[2374924]: [2026-02-02 15:29:13] [QUERY] type=REQ sub=4dTrKUBsZ7-IGFvQ6R9ZG ip=23.234.109.54 time=496us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:13 VM410 c_relay[2374924]: [2026-02-02 15:29:13] [QUERY] type=REQ sub=global_users_560cbdcbd830_0 ip=146.70.174.221 time=817us rows=1 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002,10050,10051) AND pubkey IN (?) ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:29:15 VM410 c_relay[2374924]: [2026-02-02 15:29:15] [QUERY] type=REQ sub=global_users_560cbdcbd830_0 ip=162.120.198.188 time=106us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (0,10002,10050,10051) AND pubkey IN (?) AND created_at >= 1769666844 ORDER BY created_at DESC LIMIT 500
|
||||
Feb 02 15:29:19 VM410 c_relay[2374924]: [2026-02-02 15:29:19] [QUERY] type=REQ sub=SCKWoOZaYB7Vbh4YiKIuM ip=23.234.109.54 time=171us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:19 VM410 c_relay[2374924]: [2026-02-02 15:29:19] [QUERY] type=REQ sub=SCKWoOZaYB7Vbh4YiKIuM ip=23.234.109.54 time=123us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (1111) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
Feb 02 15:29:19 VM410 c_relay[2374924]: [2026-02-02 15:29:19] [QUERY] type=REQ sub=ZlTWBGP-hlDwxsN3arTdl ip=23.234.109.54 time=525us rows=0 sql=SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000) AND kind IN (9735) AND EXISTS (SELECT 1 FROM json_each(json(tags)) WHERE json_extract(value, '$[0]') = ? AND json_extract(value, '$[1]') IN (?)) AND created_at >= 1769868430 ORDER BY created_at DESC LIMIT 100
|
||||
^Cubuntu@VM410:~$
|
||||
|
||||
95
src/api.c
95
src/api.c
@@ -1,6 +1,11 @@
|
||||
// Define _GNU_SOURCE to ensure all POSIX features are available
|
||||
#define _GNU_SOURCE
|
||||
|
||||
// Forward declaration for query logging (defined in main.c)
|
||||
extern void log_query_execution(const char* query_type, const char* sub_id,
|
||||
const char* client_ip, const char* sql,
|
||||
long elapsed_us, int rows_returned);
|
||||
|
||||
// API module for serving embedded web content and admin API functions
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -66,6 +71,10 @@ cJSON* query_event_kind_distribution(void) {
|
||||
sqlite3_stmt* stmt;
|
||||
const char* sql = "SELECT kind, COUNT(*) as count FROM events GROUP BY kind ORDER BY count DESC";
|
||||
|
||||
// Start timing
|
||||
struct timespec query_start, query_end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_start);
|
||||
|
||||
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare event kind distribution query");
|
||||
return NULL;
|
||||
@@ -77,8 +86,10 @@ cJSON* query_event_kind_distribution(void) {
|
||||
|
||||
cJSON* kinds_array = cJSON_CreateArray();
|
||||
long long total_events = 0;
|
||||
int row_count = 0;
|
||||
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
row_count++;
|
||||
int kind = sqlite3_column_int(stmt, 0);
|
||||
long long count = sqlite3_column_int64(stmt, 1);
|
||||
total_events += count;
|
||||
@@ -90,6 +101,13 @@ cJSON* query_event_kind_distribution(void) {
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Stop timing and log
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_end);
|
||||
long elapsed_us = (query_end.tv_sec - query_start.tv_sec) * 1000000L +
|
||||
(query_end.tv_nsec - query_start.tv_nsec) / 1000L;
|
||||
|
||||
log_query_execution("MONITOR", "event_kinds", NULL, sql, elapsed_us, row_count);
|
||||
|
||||
cJSON_AddNumberToObject(distribution, "total_events", total_events);
|
||||
cJSON_AddItemToObject(distribution, "kinds", kinds_array);
|
||||
@@ -239,12 +257,16 @@ cJSON* query_subscription_details(void) {
|
||||
const char* sql =
|
||||
"SELECT * "
|
||||
"FROM active_subscriptions_log "
|
||||
"ORDER BY created_at DESC LIMIT 100";
|
||||
"ORDER BY created_at DESC";
|
||||
|
||||
// DEBUG: Log the query results for debugging subscription_details
|
||||
DEBUG_LOG("=== SUBSCRIPTION_DETAILS QUERY DEBUG ===");
|
||||
DEBUG_LOG("Query: %s", sql);
|
||||
|
||||
// Start timing
|
||||
struct timespec query_start, query_end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_start);
|
||||
|
||||
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare subscription details query");
|
||||
return NULL;
|
||||
@@ -287,6 +309,46 @@ cJSON* query_subscription_details(void) {
|
||||
cJSON_AddBoolToObject(sub_obj, "active", 1); // All from this view are active
|
||||
cJSON_AddStringToObject(sub_obj, "wsi_pointer", wsi_pointer ? wsi_pointer : "N/A");
|
||||
|
||||
// Extract query stats from per_session_data if wsi is still valid
|
||||
int db_queries = 0;
|
||||
int db_rows = 0;
|
||||
double query_rate = 0.0;
|
||||
double row_rate = 0.0;
|
||||
double avg_rows_per_query = 0.0;
|
||||
|
||||
if (wsi_pointer && strlen(wsi_pointer) > 2) { // Check for valid pointer string
|
||||
// Parse wsi pointer from hex string
|
||||
struct lws* wsi = NULL;
|
||||
if (sscanf(wsi_pointer, "%p", (void**)&wsi) == 1 && wsi != NULL) {
|
||||
// Get per_session_data from wsi
|
||||
struct per_session_data* pss = (struct per_session_data*)lws_wsi_user(wsi);
|
||||
if (pss) {
|
||||
db_queries = pss->db_queries_executed;
|
||||
db_rows = pss->db_rows_returned;
|
||||
|
||||
// Calculate rates (per minute)
|
||||
time_t connection_duration = current_time - pss->query_tracking_start;
|
||||
if (connection_duration > 0) {
|
||||
double minutes = connection_duration / 60.0;
|
||||
query_rate = db_queries / minutes;
|
||||
row_rate = db_rows / minutes;
|
||||
}
|
||||
|
||||
// Calculate average rows per query
|
||||
if (db_queries > 0) {
|
||||
avg_rows_per_query = (double)db_rows / (double)db_queries;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add query stats to subscription object
|
||||
cJSON_AddNumberToObject(sub_obj, "db_queries_executed", db_queries);
|
||||
cJSON_AddNumberToObject(sub_obj, "db_rows_returned", db_rows);
|
||||
cJSON_AddNumberToObject(sub_obj, "query_rate_per_min", query_rate);
|
||||
cJSON_AddNumberToObject(sub_obj, "row_rate_per_min", row_rate);
|
||||
cJSON_AddNumberToObject(sub_obj, "avg_rows_per_query", avg_rows_per_query);
|
||||
|
||||
// Parse and add filter JSON if available
|
||||
if (filter_json) {
|
||||
cJSON* filters = cJSON_Parse(filter_json);
|
||||
@@ -311,8 +373,15 @@ cJSON* query_subscription_details(void) {
|
||||
|
||||
cJSON_AddItemToObject(subscriptions_data, "data", data);
|
||||
|
||||
// Stop timing and log
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_end);
|
||||
long elapsed_us = (query_end.tv_sec - query_start.tv_sec) * 1000000L +
|
||||
(query_end.tv_nsec - query_start.tv_nsec) / 1000L;
|
||||
|
||||
log_query_execution("MONITOR", "subscription_details", NULL, sql, elapsed_us, row_count);
|
||||
|
||||
// DEBUG: Log final summary
|
||||
DEBUG_LOG("Total subscriptions found: %d", cJSON_GetArraySize(subscriptions_array));
|
||||
DEBUG_LOG("Total subscriptions found: %d", row_count);
|
||||
DEBUG_LOG("=== END SUBSCRIPTION_DETAILS QUERY DEBUG ===");
|
||||
|
||||
return subscriptions_data;
|
||||
@@ -459,10 +528,15 @@ int generate_monitoring_event_for_type(const char* d_tag_value, cJSON* (*query_f
|
||||
|
||||
// Monitoring hook called when an event is stored
|
||||
void monitoring_on_event_stored(void) {
|
||||
// Check throttling first (cheapest check)
|
||||
// Check if monitoring is disabled (throttle = 0)
|
||||
int throttle_seconds = get_monitoring_throttle_seconds();
|
||||
if (throttle_seconds == 0) {
|
||||
return; // Monitoring disabled
|
||||
}
|
||||
|
||||
// Check throttling
|
||||
static time_t last_monitoring_time = 0;
|
||||
time_t current_time = time(NULL);
|
||||
int throttle_seconds = get_monitoring_throttle_seconds();
|
||||
|
||||
if (current_time - last_monitoring_time < throttle_seconds) {
|
||||
return;
|
||||
@@ -481,10 +555,15 @@ void monitoring_on_event_stored(void) {
|
||||
|
||||
// Monitoring hook called when subscriptions change (create/close)
|
||||
void monitoring_on_subscription_change(void) {
|
||||
// Check throttling first (cheapest check)
|
||||
// Check if monitoring is disabled (throttle = 0)
|
||||
int throttle_seconds = get_monitoring_throttle_seconds();
|
||||
if (throttle_seconds == 0) {
|
||||
return; // Monitoring disabled
|
||||
}
|
||||
|
||||
// Check throttling
|
||||
static time_t last_monitoring_time = 0;
|
||||
time_t current_time = time(NULL);
|
||||
int throttle_seconds = get_monitoring_throttle_seconds();
|
||||
|
||||
if (current_time - last_monitoring_time < throttle_seconds) {
|
||||
return;
|
||||
@@ -2721,8 +2800,8 @@ int handle_monitoring_command(cJSON* event, const char* command, char* error_mes
|
||||
|
||||
char* endptr;
|
||||
long throttle_seconds = strtol(arg, &endptr, 10);
|
||||
if (*endptr != '\0' || throttle_seconds < 1 || throttle_seconds > 3600) {
|
||||
char* response_content = "❌ Invalid throttle value\n\nThrottle must be between 1 and 3600 seconds.";
|
||||
if (*endptr != '\0' || throttle_seconds < 0 || throttle_seconds > 3600) {
|
||||
char* response_content = "❌ Invalid throttle value\n\nThrottle must be between 0 and 3600 seconds (0 = disabled).";
|
||||
return send_admin_response(sender_pubkey, response_content, request_id, error_message, error_size, wsi);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#ifdef VERSION
|
||||
#undef VERSION
|
||||
#endif
|
||||
#ifdef VERSION_MAJOR
|
||||
#undef VERSION_MAJOR
|
||||
#endif
|
||||
#ifdef VERSION_MINOR
|
||||
#undef VERSION_MINOR
|
||||
#endif
|
||||
|
||||
@@ -65,6 +65,9 @@ static const struct {
|
||||
{"max_total_subscriptions", "5000"},
|
||||
{"max_filters_per_subscription", "10"},
|
||||
|
||||
// Connection Management
|
||||
{"max_connection_seconds", "86400"}, // 24 hours (0 = disabled)
|
||||
|
||||
// Event Processing Limits
|
||||
{"max_event_tags", "100"},
|
||||
{"max_content_length", "8196"},
|
||||
|
||||
File diff suppressed because one or more lines are too long
127
src/main.c
127
src/main.c
@@ -20,6 +20,7 @@
|
||||
#include "../nostr_core_lib/nostr_core/nostr_core.h"
|
||||
#include "../nostr_core_lib/nostr_core/nip013.h" // NIP-13: Proof of Work
|
||||
#include "../nostr_core_lib/nostr_core/nip019.h" // NIP-19: bech32-encoded entities
|
||||
#include "main.h" // Version and relay metadata
|
||||
#include "config.h" // Configuration management system
|
||||
#include "sql_schema.h" // Embedded database schema
|
||||
#include "websockets.h" // WebSocket protocol implementation
|
||||
@@ -132,7 +133,7 @@ static void free_bind_params(char** params, int count) {
|
||||
int is_authorized_admin_event(cJSON* event, char* error_message, size_t error_size);
|
||||
|
||||
// Forward declaration for NOTICE message support
|
||||
void send_notice_message(struct lws* wsi, const char* message);
|
||||
void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message);
|
||||
|
||||
// Forward declarations for NIP-42 authentication functions
|
||||
void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss);
|
||||
@@ -207,7 +208,7 @@ void signal_handler(int sig) {
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Send NOTICE message to client (NIP-01)
|
||||
void send_notice_message(struct lws* wsi, const char* message) {
|
||||
void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message) {
|
||||
if (!wsi || !message) return;
|
||||
|
||||
cJSON* notice_msg = cJSON_CreateArray();
|
||||
@@ -218,7 +219,7 @@ void send_notice_message(struct lws* wsi, const char* message) {
|
||||
if (msg_str) {
|
||||
size_t msg_len = strlen(msg_str);
|
||||
// Use proper message queue system instead of direct lws_write
|
||||
if (queue_message(wsi, NULL, msg_str, msg_len, LWS_WRITE_TEXT) != 0) {
|
||||
if (queue_message(wsi, pss, msg_str, msg_len, LWS_WRITE_TEXT) != 0) {
|
||||
DEBUG_ERROR("Failed to queue NOTICE message");
|
||||
}
|
||||
free(msg_str);
|
||||
@@ -228,6 +229,65 @@ void send_notice_message(struct lws* wsi, const char* message) {
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DATABASE QUERY LOGGING
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Log database query execution with timing and context
|
||||
* Only logs at debug level 3 (DEBUG) or higher
|
||||
* Warns if query takes >10ms (slow query)
|
||||
*
|
||||
* @param query_type Type of query (REQ, COUNT, INSERT, CONFIG, etc.)
|
||||
* @param sub_id Subscription ID (NULL if not applicable)
|
||||
* @param client_ip Client IP address (NULL if not applicable)
|
||||
* @param sql SQL query text
|
||||
* @param elapsed_us Execution time in microseconds
|
||||
* @param rows_returned Number of rows returned or affected
|
||||
*/
|
||||
void log_query_execution(const char* query_type, const char* sub_id,
|
||||
const char* client_ip, const char* sql,
|
||||
long elapsed_us, int rows_returned) {
|
||||
// Only log at debug level 3 (INFO) or higher
|
||||
if (g_debug_level < DEBUG_LEVEL_INFO) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Truncate SQL if too long (keep first 500 chars)
|
||||
char sql_truncated[512];
|
||||
if (strlen(sql) > 500) {
|
||||
snprintf(sql_truncated, sizeof(sql_truncated), "%.497s...", sql);
|
||||
} else {
|
||||
snprintf(sql_truncated, sizeof(sql_truncated), "%s", sql);
|
||||
}
|
||||
|
||||
// Get timestamp
|
||||
time_t now = time(NULL);
|
||||
struct tm* tm_info = localtime(&now);
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
|
||||
// Log query with all context (direct to stdout/stderr, not through DEBUG_LOG)
|
||||
fprintf(stderr, "[%s] [QUERY] type=%s sub=%s ip=%s time=%ldus rows=%d sql=%s\n",
|
||||
timestamp,
|
||||
query_type,
|
||||
sub_id ? sub_id : "N/A",
|
||||
client_ip ? client_ip : "N/A",
|
||||
elapsed_us,
|
||||
rows_returned,
|
||||
sql_truncated);
|
||||
|
||||
// Warn if query is slow (>10ms = 10000us)
|
||||
if (elapsed_us > 10000) {
|
||||
fprintf(stderr, "[%s] [SLOW_QUERY] %ldms: %s\n",
|
||||
timestamp, elapsed_us / 1000, sql_truncated);
|
||||
}
|
||||
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// DATABASE FUNCTIONS
|
||||
@@ -369,12 +429,19 @@ int init_database(const char* database_path_override) {
|
||||
if (!db_version || strcmp(db_version, "5") == 0) {
|
||||
needs_migration = 1;
|
||||
} else if (strcmp(db_version, "6") == 0) {
|
||||
// Database is already at current schema version v6
|
||||
// Database is at schema version v6 (compatible)
|
||||
} else if (strcmp(db_version, "7") == 0) {
|
||||
// Database is at schema version v7 (compatible)
|
||||
} else if (strcmp(db_version, "8") == 0) {
|
||||
// Database is at schema version v8 (compatible)
|
||||
} else if (strcmp(db_version, "9") == 0) {
|
||||
// Database is at schema version v9 (compatible)
|
||||
} else if (strcmp(db_version, EMBEDDED_SCHEMA_VERSION) == 0) {
|
||||
// Database is at current schema version
|
||||
} else {
|
||||
char warning_msg[256];
|
||||
snprintf(warning_msg, sizeof(warning_msg), "Unknown database schema version: %s", db_version);
|
||||
snprintf(warning_msg, sizeof(warning_msg), "Unknown database schema version: %s (expected %s)",
|
||||
db_version, EMBEDDED_SCHEMA_VERSION);
|
||||
DEBUG_WARN(warning_msg);
|
||||
}
|
||||
} else {
|
||||
@@ -1191,6 +1258,10 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
snprintf(sql_ptr, remaining, " LIMIT 500");
|
||||
}
|
||||
|
||||
// Start query timing
|
||||
struct timespec query_start, query_end;
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_start);
|
||||
|
||||
// Execute query and send events
|
||||
sqlite3_stmt* stmt;
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
@@ -1198,9 +1269,30 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
char error_msg[256];
|
||||
snprintf(error_msg, sizeof(error_msg), "Failed to prepare subscription query: %s", sqlite3_errmsg(g_db));
|
||||
DEBUG_ERROR(error_msg);
|
||||
|
||||
// Log the failed query so we can see what SQL was generated
|
||||
if (g_debug_level >= DEBUG_LEVEL_DEBUG) {
|
||||
time_t now = time(NULL);
|
||||
struct tm* tm_info = localtime(&now);
|
||||
char timestamp[32];
|
||||
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
|
||||
fprintf(stderr, "[%s] [QUERY_FAILED] type=REQ sub=%s ip=%s error=%s sql=%s\n",
|
||||
timestamp,
|
||||
sub_id,
|
||||
pss ? pss->client_ip : "N/A",
|
||||
sqlite3_errmsg(g_db),
|
||||
sql);
|
||||
fflush(stderr);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track query execution for abuse detection
|
||||
if (pss) {
|
||||
pss->db_queries_executed++;
|
||||
}
|
||||
|
||||
// Bind parameters
|
||||
for (int i = 0; i < bind_param_count; i++) {
|
||||
sqlite3_bind_text(stmt, i + 1, bind_params[i], -1, SQLITE_TRANSIENT);
|
||||
@@ -1210,6 +1302,11 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
while (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
row_count++;
|
||||
|
||||
// Track rows returned for abuse detection
|
||||
if (pss) {
|
||||
pss->db_rows_returned++;
|
||||
}
|
||||
|
||||
// Build event JSON
|
||||
cJSON* event = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(event, "id", (char*)sqlite3_column_text(stmt, 0));
|
||||
@@ -1264,6 +1361,14 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
|
||||
}
|
||||
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
// Stop query timing and log
|
||||
clock_gettime(CLOCK_MONOTONIC, &query_end);
|
||||
long elapsed_us = (query_end.tv_sec - query_start.tv_sec) * 1000000L +
|
||||
(query_end.tv_nsec - query_start.tv_nsec) / 1000L;
|
||||
|
||||
log_query_execution("REQ", sub_id, pss ? pss->client_ip : NULL,
|
||||
sql, elapsed_us, row_count);
|
||||
}
|
||||
|
||||
// Cleanup bind params
|
||||
@@ -1426,7 +1531,7 @@ void print_usage(const char* program_name) {
|
||||
|
||||
// Print version information
|
||||
void print_version() {
|
||||
printf("C Nostr Relay Server v1.0.0\n");
|
||||
printf("C Nostr Relay Server %s\n", CRELAY_VERSION);
|
||||
printf("Event-based configuration system\n");
|
||||
printf("Built with nostr_core_lib integration\n");
|
||||
printf("\n");
|
||||
@@ -1562,6 +1667,10 @@ int main(int argc, char* argv[]) {
|
||||
signal(SIGINT, signal_handler);
|
||||
signal(SIGTERM, signal_handler);
|
||||
|
||||
// Print version at startup (always, regardless of debug level)
|
||||
fprintf(stderr, "[RELAY_VERSION] C Nostr Relay Server %s\n", CRELAY_VERSION);
|
||||
fflush(stderr);
|
||||
|
||||
printf(BLUE BOLD "=== C Nostr Relay Server ===" RESET "\n");
|
||||
|
||||
|
||||
@@ -1807,7 +1916,11 @@ int main(int argc, char* argv[]) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Initialize kind-based index for fast subscription lookup
|
||||
init_kind_index();
|
||||
|
||||
// Cleanup orphaned subscriptions from previous runs
|
||||
cleanup_all_subscriptions_on_startup();
|
||||
|
||||
// Start WebSocket Nostr relay server (port from CLI override or configuration)
|
||||
int result = start_websocket_relay(cli_options.port_override, cli_options.strict_port); // Use CLI port override if specified, otherwise config
|
||||
|
||||
11
src/main.h
11
src/main.h
@@ -10,17 +10,18 @@
|
||||
#define MAIN_H
|
||||
|
||||
// Version information (auto-updated by build system)
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 8
|
||||
#define VERSION_PATCH 6
|
||||
#define VERSION "v0.8.6"
|
||||
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
|
||||
#define CRELAY_VERSION_MAJOR 1
|
||||
#define CRELAY_VERSION_MINOR 1
|
||||
#define CRELAY_VERSION_PATCH 9
|
||||
#define CRELAY_VERSION "v1.1.9"
|
||||
|
||||
// Relay metadata (authoritative source for NIP-11 information)
|
||||
#define RELAY_NAME "C-Relay"
|
||||
#define RELAY_DESCRIPTION "High-performance C Nostr relay with SQLite storage"
|
||||
#define RELAY_CONTACT ""
|
||||
#define RELAY_SOFTWARE "https://git.laantungir.net/laantungir/c-relay.git"
|
||||
#define RELAY_VERSION VERSION // Use the same version as the build
|
||||
#define RELAY_VERSION CRELAY_VERSION // Use the same version as the build
|
||||
#define SUPPORTED_NIPS "1,2,4,9,11,12,13,15,16,20,22,33,40,42,50,70"
|
||||
#define LANGUAGE_TAGS ""
|
||||
#define RELAY_COUNTRIES ""
|
||||
|
||||
14
src/nip042.c
14
src/nip042.c
@@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
// Forward declaration for notice message function
|
||||
void send_notice_message(struct lws* wsi, const char* message);
|
||||
void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message);
|
||||
|
||||
// Forward declarations for NIP-42 functions from request_validator.c
|
||||
int nostr_nip42_generate_challenge(char *challenge_buffer, size_t buffer_size);
|
||||
@@ -34,7 +34,7 @@ void send_nip42_auth_challenge(struct lws* wsi, struct per_session_data* pss) {
|
||||
char challenge[65];
|
||||
if (nostr_nip42_generate_challenge(challenge, sizeof(challenge)) != 0) {
|
||||
DEBUG_ERROR("Failed to generate NIP-42 challenge");
|
||||
send_notice_message(wsi, "Authentication temporarily unavailable");
|
||||
send_notice_message(wsi, pss, "Authentication temporarily unavailable");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps
|
||||
// Serialize event for validation
|
||||
char* event_json = cJSON_Print(auth_event);
|
||||
if (!event_json) {
|
||||
send_notice_message(wsi, "Invalid authentication event format");
|
||||
send_notice_message(wsi, pss, "Invalid authentication event format");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps
|
||||
time_t current_time = time(NULL);
|
||||
if (current_time > challenge_expires) {
|
||||
free(event_json);
|
||||
send_notice_message(wsi, "Authentication challenge expired, please retry");
|
||||
send_notice_message(wsi, pss, "Authentication challenge expired, please retry");
|
||||
DEBUG_WARN("NIP-42 authentication failed: challenge expired");
|
||||
return;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps
|
||||
pss->auth_challenge_sent = 0;
|
||||
pthread_mutex_unlock(&pss->session_lock);
|
||||
|
||||
send_notice_message(wsi, "NIP-42 authentication successful");
|
||||
send_notice_message(wsi, pss, "NIP-42 authentication successful");
|
||||
} else {
|
||||
// Authentication failed
|
||||
char error_msg[256];
|
||||
@@ -135,7 +135,7 @@ void handle_nip42_auth_signed_event(struct lws* wsi, struct per_session_data* ps
|
||||
"NIP-42 authentication failed (error code: %d)", result);
|
||||
DEBUG_WARN(error_msg);
|
||||
|
||||
send_notice_message(wsi, "NIP-42 authentication failed - invalid signature or challenge");
|
||||
send_notice_message(wsi, pss, "NIP-42 authentication failed - invalid signature or challenge");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,5 +146,5 @@ void handle_nip42_auth_challenge_response(struct lws* wsi, struct per_session_da
|
||||
// NIP-42 doesn't typically use challenge responses from client to server
|
||||
// This is reserved for potential future use or protocol extensions
|
||||
DEBUG_WARN("Received unexpected challenge response from client (not part of standard NIP-42 flow)");
|
||||
send_notice_message(wsi, "Challenge responses are not supported - please send signed authentication event");
|
||||
send_notice_message(wsi, pss, "Challenge responses are not supported - please send signed authentication event");
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* Embedded SQL Schema for C Nostr Relay
|
||||
* Schema Version: 8
|
||||
* Schema Version: 10
|
||||
*/
|
||||
#ifndef SQL_SCHEMA_H
|
||||
#define SQL_SCHEMA_H
|
||||
|
||||
/* Schema version constant */
|
||||
#define EMBEDDED_SCHEMA_VERSION "8"
|
||||
#define EMBEDDED_SCHEMA_VERSION "10"
|
||||
|
||||
/* Embedded SQL schema as C string literal */
|
||||
static const char* const EMBEDDED_SCHEMA_SQL =
|
||||
@@ -14,7 +14,7 @@ static const char* const EMBEDDED_SCHEMA_SQL =
|
||||
-- Configuration system using config table\n\
|
||||
\n\
|
||||
-- Schema version tracking\n\
|
||||
PRAGMA user_version = 8;\n\
|
||||
PRAGMA user_version = 10;\n\
|
||||
\n\
|
||||
-- Enable foreign key support\n\
|
||||
PRAGMA foreign_keys = ON;\n\
|
||||
@@ -57,8 +57,8 @@ CREATE TABLE schema_info (\n\
|
||||
\n\
|
||||
-- Insert schema metadata\n\
|
||||
INSERT INTO schema_info (key, value) VALUES\n\
|
||||
('version', '8'),\n\
|
||||
('description', 'Hybrid Nostr relay schema with subscription deduplication support'),\n\
|
||||
('version', '10'),\n\
|
||||
('description', 'Added composite index for active_subscriptions_log view optimization'),\n\
|
||||
('created_at', strftime('%s', 'now'));\n\
|
||||
\n\
|
||||
-- Helper views for common queries\n\
|
||||
@@ -206,6 +206,10 @@ CREATE INDEX idx_subscriptions_created ON subscriptions(created_at DESC);\n\
|
||||
CREATE INDEX idx_subscriptions_client ON subscriptions(client_ip);\n\
|
||||
CREATE INDEX idx_subscriptions_wsi ON subscriptions(wsi_pointer);\n\
|
||||
\n\
|
||||
-- Composite index for active_subscriptions_log view optimization\n\
|
||||
-- Optimizes: WHERE event_type = 'created' AND ended_at IS NULL ORDER BY created_at DESC\n\
|
||||
CREATE INDEX idx_subscriptions_active_log ON subscriptions(event_type, ended_at, created_at DESC);\n\
|
||||
\n\
|
||||
CREATE INDEX idx_subscription_metrics_date ON subscription_metrics(date DESC);\n\
|
||||
\n\
|
||||
\n\
|
||||
@@ -236,21 +240,16 @@ ORDER BY date DESC;\n\
|
||||
-- View for current active subscriptions (from log perspective)\n\
|
||||
CREATE VIEW active_subscriptions_log AS\n\
|
||||
SELECT\n\
|
||||
s.subscription_id,\n\
|
||||
s.client_ip,\n\
|
||||
s.filter_json,\n\
|
||||
s.events_sent,\n\
|
||||
s.created_at,\n\
|
||||
(strftime('%s', 'now') - s.created_at) as duration_seconds,\n\
|
||||
s.wsi_pointer\n\
|
||||
FROM subscriptions s\n\
|
||||
WHERE s.event_type = 'created'\n\
|
||||
AND NOT EXISTS (\n\
|
||||
SELECT 1 FROM subscriptions s2\n\
|
||||
WHERE s2.subscription_id = s.subscription_id\n\
|
||||
AND s2.wsi_pointer = s.wsi_pointer\n\
|
||||
AND s2.event_type IN ('closed', 'expired', 'disconnected')\n\
|
||||
);\n\
|
||||
subscription_id,\n\
|
||||
client_ip,\n\
|
||||
filter_json,\n\
|
||||
events_sent,\n\
|
||||
created_at,\n\
|
||||
(strftime('%s', 'now') - created_at) as duration_seconds,\n\
|
||||
wsi_pointer\n\
|
||||
FROM subscriptions\n\
|
||||
WHERE event_type = 'created'\n\
|
||||
AND ended_at IS NULL;\n\
|
||||
\n\
|
||||
-- Database Statistics Views for Admin API\n\
|
||||
-- Event kinds distribution view\n\
|
||||
|
||||
@@ -37,6 +37,135 @@ extern int get_config_bool(const char* key, int default_value);
|
||||
// Global subscription manager
|
||||
extern subscription_manager_t g_subscription_manager;
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
// KIND-BASED INDEX FOR FAST SUBSCRIPTION LOOKUP
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Initialize the kind index (called once at startup)
|
||||
void init_kind_index() {
|
||||
DEBUG_LOG("Initializing kind index for 65536 possible kinds");
|
||||
|
||||
// Initialize all kind index entries to NULL
|
||||
for (int i = 0; i < 65536; i++) {
|
||||
g_subscription_manager.kind_index[i] = NULL;
|
||||
}
|
||||
|
||||
// Initialize no-kind-filter list
|
||||
g_subscription_manager.no_kind_filter_subs = NULL;
|
||||
|
||||
DEBUG_LOG("Kind index initialized successfully");
|
||||
}
|
||||
|
||||
// Add a subscription to the kind index for all kinds it's interested in
|
||||
// Must be called with subscriptions_lock held
|
||||
void add_subscription_to_kind_index(subscription_t* sub) {
|
||||
if (!sub) return;
|
||||
|
||||
int has_kind_filter = 0;
|
||||
|
||||
// Track which kinds we've already added to avoid duplicates
|
||||
// Use a bitmap for memory efficiency: 65536 bits = 8192 bytes
|
||||
unsigned char added_kinds[8192] = {0}; // 65536 / 8 = 8192 bytes
|
||||
|
||||
// Iterate through all filters in this subscription
|
||||
subscription_filter_t* filter = sub->filters;
|
||||
while (filter) {
|
||||
// Check if this filter has a kinds array
|
||||
if (filter->kinds && cJSON_IsArray(filter->kinds)) {
|
||||
has_kind_filter = 1;
|
||||
|
||||
// Add subscription to index for each kind in the filter
|
||||
cJSON* kind_item = NULL;
|
||||
cJSON_ArrayForEach(kind_item, filter->kinds) {
|
||||
if (cJSON_IsNumber(kind_item)) {
|
||||
int kind = (int)cJSON_GetNumberValue(kind_item);
|
||||
|
||||
// Bounds check
|
||||
if (kind < 0 || kind > 65535) {
|
||||
DEBUG_WARN("add_subscription_to_kind_index: kind %d out of range, skipping", kind);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if we've already added this kind (deduplication)
|
||||
int byte_index = kind / 8;
|
||||
int bit_index = kind % 8;
|
||||
if (added_kinds[byte_index] & (1 << bit_index)) {
|
||||
DEBUG_TRACE("KIND_INDEX: Skipping duplicate kind %d for subscription '%s'", kind, sub->id);
|
||||
continue; // Already added this kind
|
||||
}
|
||||
|
||||
// Mark this kind as added
|
||||
added_kinds[byte_index] |= (1 << bit_index);
|
||||
|
||||
// Create new index node
|
||||
kind_subscription_node_t* node = malloc(sizeof(kind_subscription_node_t));
|
||||
if (!node) {
|
||||
DEBUG_ERROR("add_subscription_to_kind_index: failed to allocate node for kind %d", kind);
|
||||
continue;
|
||||
}
|
||||
|
||||
node->subscription = sub;
|
||||
node->next = g_subscription_manager.kind_index[kind];
|
||||
g_subscription_manager.kind_index[kind] = node;
|
||||
|
||||
DEBUG_TRACE("KIND_INDEX: Added subscription '%s' to kind %d index", sub->id, kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
filter = filter->next;
|
||||
}
|
||||
|
||||
// If subscription has no kind filter, add to no-kind-filter list using wrapper node
|
||||
if (!has_kind_filter) {
|
||||
no_kind_filter_node_t* node = malloc(sizeof(no_kind_filter_node_t));
|
||||
if (!node) {
|
||||
DEBUG_ERROR("add_subscription_to_kind_index: failed to allocate no-kind-filter node");
|
||||
return;
|
||||
}
|
||||
|
||||
node->subscription = sub;
|
||||
node->next = g_subscription_manager.no_kind_filter_subs;
|
||||
g_subscription_manager.no_kind_filter_subs = node;
|
||||
DEBUG_TRACE("KIND_INDEX: Added subscription '%s' to no-kind-filter list", sub->id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove a subscription from the kind index
|
||||
// Must be called with subscriptions_lock held
|
||||
void remove_subscription_from_kind_index(subscription_t* sub) {
|
||||
if (!sub) return;
|
||||
|
||||
// Remove from all kind indexes
|
||||
for (int kind = 0; kind < 65536; kind++) {
|
||||
kind_subscription_node_t** current = &g_subscription_manager.kind_index[kind];
|
||||
|
||||
while (*current) {
|
||||
if ((*current)->subscription == sub) {
|
||||
kind_subscription_node_t* to_free = *current;
|
||||
*current = (*current)->next;
|
||||
free(to_free);
|
||||
DEBUG_TRACE("KIND_INDEX: Removed subscription '%s' from kind %d index", sub->id, kind);
|
||||
// Don't break - subscription might be in index multiple times if it has duplicate kinds
|
||||
} else {
|
||||
current = &((*current)->next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from no-kind-filter list if present
|
||||
no_kind_filter_node_t** current = &g_subscription_manager.no_kind_filter_subs;
|
||||
while (*current) {
|
||||
if ((*current)->subscription == sub) {
|
||||
no_kind_filter_node_t* to_free = *current;
|
||||
*current = (*current)->next;
|
||||
free(to_free);
|
||||
DEBUG_TRACE("KIND_INDEX: Removed subscription '%s' from no-kind-filter list", sub->id);
|
||||
break;
|
||||
}
|
||||
current = &((*current)->next);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -284,6 +413,14 @@ int add_subscription_to_manager(subscription_t* sub) {
|
||||
g_subscription_manager.total_created++;
|
||||
}
|
||||
|
||||
// Add to kind index for fast lookup (must be done while holding lock)
|
||||
add_subscription_to_kind_index(sub);
|
||||
|
||||
// If we found a duplicate, remove it from the kind index
|
||||
if (duplicate_old) {
|
||||
remove_subscription_from_kind_index(duplicate_old);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
|
||||
|
||||
// If we replaced an existing subscription, unlink it from the per-session list before freeing
|
||||
@@ -341,6 +478,9 @@ int remove_subscription_from_manager(const char* sub_id, struct lws* wsi) {
|
||||
|
||||
// Match by ID and WebSocket connection
|
||||
if (strcmp(sub->id, sub_id) == 0 && (!wsi || sub->wsi == wsi)) {
|
||||
// Remove from kind index first (while still holding lock)
|
||||
remove_subscription_from_kind_index(sub);
|
||||
|
||||
// Remove from list
|
||||
*current = sub->next;
|
||||
g_subscription_manager.total_subscriptions--;
|
||||
@@ -654,19 +794,47 @@ int broadcast_event_to_subscriptions(cJSON* event) {
|
||||
temp_sub_t* matching_subs = NULL;
|
||||
int matching_count = 0;
|
||||
|
||||
// Get event kind for index lookup
|
||||
int event_kind_val = -1;
|
||||
if (event_kind && cJSON_IsNumber(event_kind)) {
|
||||
event_kind_val = (int)cJSON_GetNumberValue(event_kind);
|
||||
}
|
||||
|
||||
// First pass: collect matching subscriptions while holding lock
|
||||
pthread_mutex_lock(&g_subscription_manager.subscriptions_lock);
|
||||
|
||||
int total_subs = 0;
|
||||
subscription_t* count_sub = g_subscription_manager.active_subscriptions;
|
||||
while (count_sub) {
|
||||
total_subs++;
|
||||
count_sub = count_sub->next;
|
||||
}
|
||||
DEBUG_TRACE("BROADCAST: Checking %d active subscriptions", total_subs);
|
||||
// Use kind index for fast lookup instead of checking all subscriptions
|
||||
subscription_t* candidates_to_check[MAX_TOTAL_SUBSCRIPTIONS];
|
||||
int candidate_count = 0;
|
||||
|
||||
subscription_t* sub = g_subscription_manager.active_subscriptions;
|
||||
while (sub) {
|
||||
// Add subscriptions from kind index (if event has valid kind)
|
||||
if (event_kind_val >= 0 && event_kind_val <= 65535) {
|
||||
DEBUG_TRACE("BROADCAST: Using kind index for kind=%d", event_kind_val);
|
||||
|
||||
kind_subscription_node_t* node = g_subscription_manager.kind_index[event_kind_val];
|
||||
while (node && candidate_count < MAX_TOTAL_SUBSCRIPTIONS) {
|
||||
if (node->subscription && node->subscription->active) {
|
||||
candidates_to_check[candidate_count++] = node->subscription;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Add subscriptions with no kind filter (must check against all events)
|
||||
no_kind_filter_node_t* no_kind_node = g_subscription_manager.no_kind_filter_subs;
|
||||
while (no_kind_node && candidate_count < MAX_TOTAL_SUBSCRIPTIONS) {
|
||||
if (no_kind_node->subscription && no_kind_node->subscription->active) {
|
||||
candidates_to_check[candidate_count++] = no_kind_node->subscription;
|
||||
}
|
||||
no_kind_node = no_kind_node->next;
|
||||
}
|
||||
|
||||
DEBUG_TRACE("BROADCAST: Checking %d candidate subscriptions (kind index optimization)", candidate_count);
|
||||
|
||||
// Test each candidate subscription
|
||||
for (int i = 0; i < candidate_count; i++) {
|
||||
subscription_t* sub = candidates_to_check[i];
|
||||
|
||||
if (sub->active && sub->wsi && event_matches_subscription(event, sub)) {
|
||||
temp_sub_t* temp = malloc(sizeof(temp_sub_t));
|
||||
if (temp) {
|
||||
@@ -695,7 +863,6 @@ int broadcast_event_to_subscriptions(cJSON* event) {
|
||||
DEBUG_ERROR("broadcast_event_to_subscriptions: failed to allocate temp subscription");
|
||||
}
|
||||
}
|
||||
sub = sub->next;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
|
||||
@@ -999,6 +1166,44 @@ void update_subscription_events_sent(const char* sub_id, int events_sent) {
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup all subscriptions on startup
|
||||
void cleanup_all_subscriptions_on_startup(void) {
|
||||
if (!g_db) {
|
||||
DEBUG_ERROR("Database not available for startup cleanup");
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG("Performing startup subscription cleanup");
|
||||
|
||||
// Mark all active subscriptions as disconnected
|
||||
const char* sql =
|
||||
"UPDATE subscriptions "
|
||||
"SET ended_at = strftime('%s', 'now') "
|
||||
"WHERE event_type = 'created' AND ended_at IS NULL";
|
||||
|
||||
sqlite3_stmt* stmt;
|
||||
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
|
||||
if (rc != SQLITE_OK) {
|
||||
DEBUG_ERROR("Failed to prepare startup cleanup query");
|
||||
return;
|
||||
}
|
||||
|
||||
rc = sqlite3_step(stmt);
|
||||
int changes = sqlite3_changes(g_db);
|
||||
sqlite3_finalize(stmt);
|
||||
|
||||
if (rc != SQLITE_DONE) {
|
||||
DEBUG_ERROR("Failed to execute startup cleanup");
|
||||
return;
|
||||
}
|
||||
|
||||
if (changes > 0) {
|
||||
DEBUG_LOG("Startup cleanup: marked %d orphaned subscriptions as disconnected", changes);
|
||||
} else {
|
||||
DEBUG_LOG("Startup cleanup: no orphaned subscriptions found");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -63,6 +63,18 @@ struct subscription {
|
||||
struct subscription* session_next; // Next subscription for this session
|
||||
};
|
||||
|
||||
// Kind index entry - linked list of subscriptions interested in a specific kind
|
||||
typedef struct kind_subscription_node {
|
||||
subscription_t* subscription; // Pointer to subscription
|
||||
struct kind_subscription_node* next; // Next subscription for this kind
|
||||
} kind_subscription_node_t;
|
||||
|
||||
// No-kind-filter list entry - wrapper to avoid corrupting subscription->next pointer
|
||||
typedef struct no_kind_filter_node {
|
||||
subscription_t* subscription; // Pointer to subscription
|
||||
struct no_kind_filter_node* next; // Next subscription in no-kind list
|
||||
} no_kind_filter_node_t;
|
||||
|
||||
// Per-IP connection tracking
|
||||
typedef struct ip_connection_info {
|
||||
char ip_address[CLIENT_IP_MAX_LENGTH]; // IP address
|
||||
@@ -79,6 +91,10 @@ struct subscription_manager {
|
||||
pthread_mutex_t subscriptions_lock; // Global thread safety
|
||||
int total_subscriptions; // Current count
|
||||
|
||||
// Kind-based index for fast subscription lookup (10x performance improvement)
|
||||
kind_subscription_node_t* kind_index[65536]; // Array of subscription lists, one per kind
|
||||
no_kind_filter_node_t* no_kind_filter_subs; // Subscriptions with no kind filter (wrapper nodes)
|
||||
|
||||
// Configuration
|
||||
int max_subscriptions_per_client; // Default: 20
|
||||
int max_total_subscriptions; // Default: 5000
|
||||
@@ -104,6 +120,11 @@ int event_matches_filter(cJSON* event, subscription_filter_t* filter);
|
||||
int event_matches_subscription(cJSON* event, subscription_t* subscription);
|
||||
int broadcast_event_to_subscriptions(cJSON* event);
|
||||
|
||||
// Kind index functions for performance optimization
|
||||
void init_kind_index(void);
|
||||
void add_subscription_to_kind_index(subscription_t* sub);
|
||||
void remove_subscription_from_kind_index(subscription_t* sub);
|
||||
|
||||
// Per-IP connection tracking functions
|
||||
ip_connection_info_t* get_or_create_ip_connection(const char* client_ip);
|
||||
void update_ip_connection_activity(const char* client_ip);
|
||||
@@ -120,4 +141,7 @@ void update_subscription_events_sent(const char* sub_id, int events_sent);
|
||||
// Subscription query functions
|
||||
int has_subscriptions_for_kind(int event_kind);
|
||||
|
||||
// Startup cleanup function
|
||||
void cleanup_all_subscriptions_on_startup(void);
|
||||
|
||||
#endif // SUBSCRIPTIONS_H
|
||||
870
src/websockets.c
870
src/websockets.c
File diff suppressed because it is too large
Load Diff
@@ -21,10 +21,10 @@
|
||||
|
||||
// Filter validation constants
|
||||
#define MAX_FILTERS_PER_REQUEST 10
|
||||
#define MAX_AUTHORS_PER_FILTER 100
|
||||
#define MAX_IDS_PER_FILTER 100
|
||||
#define MAX_KINDS_PER_FILTER 50
|
||||
#define MAX_TAG_VALUES_PER_FILTER 100
|
||||
#define MAX_AUTHORS_PER_FILTER 1000
|
||||
#define MAX_IDS_PER_FILTER 1000
|
||||
#define MAX_KINDS_PER_FILTER 500
|
||||
#define MAX_TAG_VALUES_PER_FILTER 1000
|
||||
#define MAX_KIND_VALUE 65535
|
||||
#define MAX_TIMESTAMP_VALUE 2147483647 // Max 32-bit signed int
|
||||
#define MAX_LIMIT_VALUE 5000
|
||||
@@ -73,6 +73,17 @@ struct per_session_data {
|
||||
struct message_queue_node* message_queue_tail; // Tail of message queue
|
||||
int message_queue_count; // Number of messages in queue
|
||||
int writeable_requested; // Flag: 1 if writeable callback requested
|
||||
|
||||
// Message reassembly for handling fragmented WebSocket messages
|
||||
char* reassembly_buffer; // Buffer for accumulating message fragments (NULL when not reassembling)
|
||||
size_t reassembly_size; // Current size of accumulated data
|
||||
size_t reassembly_capacity; // Allocated capacity of reassembly buffer
|
||||
int reassembly_active; // Flag: 1 if currently reassembling a message
|
||||
|
||||
// Database query tracking for abuse detection and monitoring
|
||||
int db_queries_executed; // Total SELECT queries executed by this connection
|
||||
int db_rows_returned; // Total rows returned across all queries
|
||||
time_t query_tracking_start; // When connection was established (for rate calculation)
|
||||
};
|
||||
|
||||
// NIP-11 HTTP session data structure for managing buffer lifetime
|
||||
|
||||
5
tests/.test_keys.txt
Normal file
5
tests/.test_keys.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
# Test key configuration (from make_and_restart_relay.sh -t)
|
||||
ADMIN_PRIVATE_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
|
||||
ADMIN_PUBLIC_KEY="6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3"
|
||||
RELAY_PUBLIC_KEY="4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa"
|
||||
RELAY_URL="ws://localhost:8888"
|
||||
101
tests/invalid_kind_test.sh
Executable file
101
tests/invalid_kind_test.sh
Executable file
@@ -0,0 +1,101 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Test for invalid kind filter validation and NOTICE response
|
||||
# This test verifies that the relay properly responds with a NOTICE message
|
||||
# when a REQ contains an invalid kind value (> 65535 per NIP-01)
|
||||
|
||||
RELAY_URL="ws://localhost:8888"
|
||||
TEST_NAME="Invalid Kind Filter Test"
|
||||
|
||||
echo "=========================================="
|
||||
echo "$TEST_NAME"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Test 1: Send REQ with invalid kind (99999 > 65535)
|
||||
echo "Test 1: REQ with invalid kind 99999 (should receive NOTICE)"
|
||||
echo "---"
|
||||
|
||||
RESPONSE=$(timeout 3 websocat "$RELAY_URL" <<EOF
|
||||
["REQ","test-invalid-kind",{"kinds":[99999],"limit":0}]
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "Response: $RESPONSE"
|
||||
|
||||
if echo "$RESPONSE" | grep -q "NOTICE"; then
|
||||
echo "✓ PASS: Received NOTICE for invalid kind"
|
||||
if echo "$RESPONSE" | grep -qi "kind"; then
|
||||
echo "✓ PASS: NOTICE mentions kind validation"
|
||||
else
|
||||
echo "⚠ WARNING: NOTICE doesn't mention kind (but NOTICE was sent)"
|
||||
fi
|
||||
else
|
||||
echo "✗ FAIL: No NOTICE received for invalid kind"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Test 2: Send REQ with valid kind (should receive EOSE)
|
||||
echo "Test 2: REQ with valid kind 1 (should receive EOSE)"
|
||||
echo "---"
|
||||
|
||||
RESPONSE=$(timeout 3 websocat "$RELAY_URL" <<EOF
|
||||
["REQ","test-valid-kind",{"kinds":[1],"limit":0}]
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "Response: $RESPONSE"
|
||||
|
||||
if echo "$RESPONSE" | grep -q "EOSE"; then
|
||||
echo "✓ PASS: Received EOSE for valid kind"
|
||||
else
|
||||
echo "✗ FAIL: No EOSE received for valid kind"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Test 3: Send REQ with kind at boundary (65535 - should be valid)
|
||||
echo "Test 3: REQ with boundary kind 65535 (should receive EOSE)"
|
||||
echo "---"
|
||||
|
||||
RESPONSE=$(timeout 3 websocat "$RELAY_URL" <<EOF
|
||||
["REQ","test-boundary-kind",{"kinds":[65535],"limit":0}]
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "Response: $RESPONSE"
|
||||
|
||||
if echo "$RESPONSE" | grep -q "EOSE"; then
|
||||
echo "✓ PASS: Received EOSE for boundary kind 65535"
|
||||
else
|
||||
echo "✗ FAIL: No EOSE received for boundary kind"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Test 4: Send REQ with kind just over boundary (65536 - should receive NOTICE)
|
||||
echo "Test 4: REQ with over-boundary kind 65536 (should receive NOTICE)"
|
||||
echo "---"
|
||||
|
||||
RESPONSE=$(timeout 3 websocat "$RELAY_URL" <<EOF
|
||||
["REQ","test-over-boundary",{"kinds":[65536],"limit":0}]
|
||||
EOF
|
||||
)
|
||||
|
||||
echo "Response: $RESPONSE"
|
||||
|
||||
if echo "$RESPONSE" | grep -q "NOTICE"; then
|
||||
echo "✓ PASS: Received NOTICE for over-boundary kind"
|
||||
else
|
||||
echo "✗ FAIL: No NOTICE received for over-boundary kind"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "All tests passed!"
|
||||
echo "=========================================="
|
||||
295
tests/subscription_cleanup_test.sh
Executable file
295
tests/subscription_cleanup_test.sh
Executable file
@@ -0,0 +1,295 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Subscription Cleanup Testing Suite for C-Relay
|
||||
# Tests startup cleanup and connection age limit features
|
||||
|
||||
set -e
|
||||
|
||||
# Load test keys
|
||||
source "$(dirname "$0")/.test_keys.txt"
|
||||
|
||||
# Configuration
|
||||
RELAY_HOST="127.0.0.1"
|
||||
RELAY_PORT="8888"
|
||||
RELAY_URL="ws://${RELAY_HOST}:${RELAY_PORT}"
|
||||
|
||||
# Colors for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Test counters
|
||||
TOTAL_TESTS=0
|
||||
PASSED_TESTS=0
|
||||
FAILED_TESTS=0
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Subscription Cleanup Test Suite${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Function to print test header
|
||||
print_test_header() {
|
||||
echo -e "${BLUE}=== Test $1: $2 ===${NC}"
|
||||
}
|
||||
|
||||
# Function to print test result
|
||||
print_result() {
|
||||
local status=$1
|
||||
local message=$2
|
||||
|
||||
TOTAL_TESTS=$((TOTAL_TESTS + 1))
|
||||
|
||||
if [ "$status" = "PASS" ]; then
|
||||
echo -e "${GREEN}[PASS]${NC} $message"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
elif [ "$status" = "FAIL" ]; then
|
||||
echo -e "${RED}[FAIL]${NC} $message"
|
||||
FAILED_TESTS=$((FAILED_TESTS + 1))
|
||||
else
|
||||
echo -e "${YELLOW}[WARN]${NC} $message"
|
||||
PASSED_TESTS=$((PASSED_TESTS + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to check if relay is running
|
||||
check_relay_running() {
|
||||
# Send a simple REQ and check for EOSE response
|
||||
local response=$(echo '["REQ","ping",{}]' | timeout 2 websocat -n1 "$RELAY_URL" 2>/dev/null)
|
||||
if echo "$response" | grep -q "EOSE\|EVENT"; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to create a subscription
|
||||
create_subscription() {
|
||||
local sub_id=$1
|
||||
local filter=${2:-"{}"}
|
||||
|
||||
echo "[\"REQ\",\"$sub_id\",$filter]" | timeout 5 websocat -n1 "$RELAY_URL" 2>/dev/null || echo "TIMEOUT"
|
||||
}
|
||||
|
||||
# Function to close a subscription
|
||||
close_subscription() {
|
||||
local sub_id=$1
|
||||
|
||||
echo "[\"CLOSE\",\"$sub_id\"]" | timeout 5 websocat -n1 "$RELAY_URL" 2>/dev/null || echo "TIMEOUT"
|
||||
}
|
||||
|
||||
# Function to query subscription count from database
|
||||
get_subscription_count() {
|
||||
local db_file=$(find . -name "*.db" -type f 2>/dev/null | head -1)
|
||||
|
||||
if [ -z "$db_file" ]; then
|
||||
echo "0"
|
||||
return
|
||||
fi
|
||||
|
||||
sqlite3 "$db_file" "SELECT COUNT(*) FROM subscriptions WHERE event_type='created' AND ended_at IS NULL;" 2>/dev/null || echo "0"
|
||||
}
|
||||
|
||||
# Test 1: Basic Connectivity
|
||||
print_test_header "1" "Basic Connectivity"
|
||||
|
||||
if check_relay_running; then
|
||||
print_result "PASS" "Relay is running and accepting connections"
|
||||
else
|
||||
print_result "FAIL" "Cannot connect to relay at $RELAY_URL"
|
||||
echo ""
|
||||
echo -e "${RED}ERROR: Relay must be running for tests to proceed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 2: Create Multiple Subscriptions
|
||||
print_test_header "2" "Create Multiple Subscriptions"
|
||||
|
||||
echo "[INFO] Creating 5 test subscriptions..."
|
||||
for i in {1..5}; do
|
||||
response=$(create_subscription "cleanup_test_$i")
|
||||
if echo "$response" | grep -q "EOSE"; then
|
||||
echo "[INFO] Subscription cleanup_test_$i created successfully"
|
||||
else
|
||||
print_result "WARN" "Subscription cleanup_test_$i may not have been created: $response"
|
||||
fi
|
||||
done
|
||||
|
||||
# Give subscriptions time to be logged
|
||||
sleep 2
|
||||
|
||||
# Check subscription count in database
|
||||
active_subs=$(get_subscription_count)
|
||||
echo "[INFO] Active subscriptions in database: $active_subs"
|
||||
|
||||
if [ "$active_subs" -ge 5 ]; then
|
||||
print_result "PASS" "Multiple subscriptions created and logged ($active_subs active)"
|
||||
else
|
||||
print_result "WARN" "Expected at least 5 subscriptions, found $active_subs"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 3: Simulate Orphaned Subscriptions (disconnect without CLOSE)
|
||||
print_test_header "3" "Simulate Orphaned Subscriptions"
|
||||
|
||||
echo "[INFO] Creating subscriptions and disconnecting abruptly..."
|
||||
|
||||
# Create subscriptions in background and kill the connection
|
||||
for i in {6..10}; do
|
||||
(echo "[\"REQ\",\"orphan_test_$i\",{}]" | timeout 2 websocat "$RELAY_URL" &>/dev/null) &
|
||||
pid=$!
|
||||
sleep 0.5
|
||||
kill -9 $pid 2>/dev/null || true
|
||||
done
|
||||
|
||||
sleep 2
|
||||
|
||||
orphaned_subs=$(get_subscription_count)
|
||||
echo "[INFO] Subscriptions after abrupt disconnects: $orphaned_subs"
|
||||
|
||||
if [ "$orphaned_subs" -gt "$active_subs" ]; then
|
||||
print_result "PASS" "Orphaned subscriptions detected ($orphaned_subs total, was $active_subs)"
|
||||
else
|
||||
print_result "WARN" "No increase in orphaned subscriptions detected"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 4: Startup Cleanup (requires relay restart)
|
||||
print_test_header "4" "Startup Cleanup Feature"
|
||||
|
||||
echo "[INFO] This test requires relay restart to verify startup cleanup"
|
||||
echo "[INFO] Current orphaned subscriptions: $orphaned_subs"
|
||||
echo ""
|
||||
echo -e "${YELLOW}[ACTION REQUIRED]${NC} Please restart the relay now with:"
|
||||
echo " ./make_and_restart_relay.sh"
|
||||
echo ""
|
||||
echo -n "Press Enter after relay has restarted to continue..."
|
||||
read
|
||||
|
||||
# Wait for relay to be ready
|
||||
echo "[INFO] Waiting for relay to be ready..."
|
||||
sleep 3
|
||||
|
||||
if ! check_relay_running; then
|
||||
print_result "FAIL" "Relay not responding after restart"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if orphaned subscriptions were cleaned up
|
||||
cleaned_subs=$(get_subscription_count)
|
||||
echo "[INFO] Active subscriptions after restart: $cleaned_subs"
|
||||
|
||||
if [ "$cleaned_subs" -eq 0 ]; then
|
||||
print_result "PASS" "Startup cleanup removed all orphaned subscriptions"
|
||||
elif [ "$cleaned_subs" -lt "$orphaned_subs" ]; then
|
||||
print_result "PASS" "Startup cleanup reduced orphaned subscriptions (from $orphaned_subs to $cleaned_subs)"
|
||||
else
|
||||
print_result "FAIL" "Startup cleanup did not reduce orphaned subscriptions"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 5: Connection Age Limit (requires configuration)
|
||||
print_test_header "5" "Connection Age Limit Feature"
|
||||
|
||||
echo "[INFO] Testing connection age limit feature..."
|
||||
echo "[INFO] Default max_connection_seconds is 86400 (24 hours)"
|
||||
echo ""
|
||||
echo -e "${YELLOW}[INFO]${NC} To test connection age limit with shorter timeout:"
|
||||
echo " 1. Set max_connection_seconds to 60 (1 minute) via admin event"
|
||||
echo " 2. Create a subscription and wait 61 seconds"
|
||||
echo " 3. Connection should be automatically closed"
|
||||
echo ""
|
||||
echo "[INFO] For this test, we'll verify the feature is enabled in config"
|
||||
|
||||
# Create a test subscription to verify connection works
|
||||
response=$(create_subscription "age_test_1")
|
||||
if echo "$response" | grep -q "EOSE"; then
|
||||
print_result "PASS" "Connection age limit feature is operational (subscription created)"
|
||||
else
|
||||
print_result "WARN" "Could not verify connection age limit feature"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 6: Verify Client Reconnection
|
||||
print_test_header "6" "Client Reconnection After Cleanup"
|
||||
|
||||
echo "[INFO] Testing that clients can reconnect after cleanup..."
|
||||
|
||||
# Create a subscription
|
||||
response=$(create_subscription "reconnect_test_1")
|
||||
if echo "$response" | grep -q "EOSE"; then
|
||||
echo "[INFO] First connection successful"
|
||||
|
||||
# Close and reconnect
|
||||
sleep 1
|
||||
response=$(create_subscription "reconnect_test_2")
|
||||
if echo "$response" | grep -q "EOSE"; then
|
||||
print_result "PASS" "Client can reconnect and create new subscriptions"
|
||||
else
|
||||
print_result "FAIL" "Client reconnection failed"
|
||||
fi
|
||||
else
|
||||
print_result "FAIL" "Initial connection failed"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 7: Verify Disabled State (max_connection_seconds = 0)
|
||||
print_test_header "7" "Verify Feature Can Be Disabled"
|
||||
|
||||
echo "[INFO] Connection age limit can be disabled by setting max_connection_seconds=0"
|
||||
echo "[INFO] When disabled, connections remain open indefinitely"
|
||||
echo "[INFO] This is the recommended setting for most relays"
|
||||
|
||||
# Create a long-lived subscription
|
||||
response=$(create_subscription "disabled_test_1")
|
||||
if echo "$response" | grep -q "EOSE"; then
|
||||
print_result "PASS" "Subscriptions work normally when feature is disabled/default"
|
||||
else
|
||||
print_result "WARN" "Could not verify disabled state"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Test 8: Database Integrity Check
|
||||
print_test_header "8" "Database Integrity After Cleanup"
|
||||
|
||||
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
|
||||
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
|
||||
print_result "PASS" "Subscription table is accessible"
|
||||
else
|
||||
print_result "FAIL" "Subscription table is not accessible"
|
||||
fi
|
||||
else
|
||||
print_result "WARN" "No database file found"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# Final Summary
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE}Test Summary${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo "Total Tests: $TOTAL_TESTS"
|
||||
echo -e "${GREEN}Passed: $PASSED_TESTS${NC}"
|
||||
echo -e "${RED}Failed: $FAILED_TESTS${NC}"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED_TESTS -eq 0 ]; then
|
||||
echo -e "${GREEN}All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}Some tests failed. Please review the output above.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
747
tests/test_requests.mjs
Normal file
747
tests/test_requests.mjs
Normal file
@@ -0,0 +1,747 @@
|
||||
|
||||
/**
|
||||
* Nostr Relay Pubkey Filter Test
|
||||
* Tests how many pubkeys different relays can handle in a single filter request
|
||||
*/
|
||||
|
||||
import { WebSocket } from 'ws';
|
||||
|
||||
// Configuration
|
||||
const RELAYS = [
|
||||
// "wss://relay.laantungir.net"
|
||||
"ws://127.0.0.1:8888"
|
||||
];
|
||||
|
||||
// Test parameters
|
||||
const STEP_SIZE = 25; // Increment pubkey count by 25 each test
|
||||
const MAX_PUBKEYS = 500; // Maximum pubkeys to test
|
||||
const EVENT_KIND = 1; // Kind 1 = text notes
|
||||
const EVENT_LIMIT = 2; // Only request 2 events per test
|
||||
|
||||
// Generate test pubkey arrays of increasing sizes
|
||||
function generateTestPubkeyArrays() {
|
||||
const testArrays = [];
|
||||
for (let count = STEP_SIZE; count <= MAX_PUBKEYS; count += STEP_SIZE) {
|
||||
testArrays.push(PUBKEYS.slice(0, count));
|
||||
}
|
||||
return testArrays;
|
||||
}
|
||||
|
||||
const PUBKEYS = [
|
||||
"85080d3bad70ccdcd7f74c29a44f55bb85cbcd3dd0cbb957da1d215bdb931204",
|
||||
"82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2",
|
||||
"916b7aca250f43b9f842faccc831db4d155088632a8c27c0d140f2043331ba57",
|
||||
"2645caf5706a31767c921532975a079f85950e1006bd5065f5dd0213e6848a96",
|
||||
"8fe3f243e91121818107875d51bca4f3fcf543437aa9715150ec8036358939c5",
|
||||
"83e818dfbeccea56b0f551576b3fd39a7a50e1d8159343500368fa085ccd964b",
|
||||
"a341f45ff9758f570a21b000c17d4e53a3a497c8397f26c0e6d61e5acffc7a98",
|
||||
"e88a691e98d9987c964521dff60025f60700378a4879180dcbbb4a5027850411",
|
||||
"2cde0e02bda47eaeeed65e341619cc5f2afce990164669da4e1e5989180a96b9",
|
||||
"edcd20558f17d99327d841e4582f9b006331ac4010806efa020ef0d40078e6da",
|
||||
"34d2f5274f1958fcd2cb2463dabeaddf8a21f84ace4241da888023bf05cc8095",
|
||||
"c48b5cced5ada74db078df6b00fa53fc1139d73bf0ed16de325d52220211dbd5",
|
||||
"04c915daefee38317fa734444acee390a8269fe5810b2241e5e6dd343dfbecc9",
|
||||
"e33fe65f1fde44c6dc17eeb38fdad0fceaf1cae8722084332ed1e32496291d42",
|
||||
"1306edd66f1da374adc417cf884bbcff57c6399656236c1f872ee10403c01b2d",
|
||||
"eaf27aa104833bcd16f671488b01d65f6da30163b5848aea99677cc947dd00aa",
|
||||
"472f440f29ef996e92a186b8d320ff180c855903882e59d50de1b8bd5669301e",
|
||||
"be1d89794bf92de5dd64c1e60f6a2c70c140abac9932418fee30c5c637fe9479",
|
||||
"c49d52a573366792b9a6e4851587c28042fb24fa5625c6d67b8c95c8751aca15",
|
||||
"c037a6897df86bfd4df5496ca7e2318992b4766897fb18fbd1d347a4f4459f5e",
|
||||
"e41e883f1ef62485a074c1a1fa1d0a092a5d678ad49bedc2f955ab5e305ba94e",
|
||||
"020f2d21ae09bf35fcdfb65decf1478b846f5f728ab30c5eaabcd6d081a81c3e",
|
||||
"e2f28c1ac6dff5a7b755635af4c8436d2fec89b888a9d9548a51b2c63f779555",
|
||||
"29fbc05acee671fb579182ca33b0e41b455bb1f9564b90a3d8f2f39dee3f2779",
|
||||
"090254801a7e8e5085b02e711622f0dfa1a85503493af246aa42af08f5e4d2df",
|
||||
"3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d",
|
||||
"6b0d4c8d9dc59e110d380b0429a02891f1341a0fa2ba1b1cf83a3db4d47e3964",
|
||||
"b0b8fbd9578ac23e782d97a32b7b3a72cda0760761359bd65661d42752b4090a",
|
||||
"b7996c183e036df27802945b80bbdc8b0bf5971b6621a86bf3569c332117f07d",
|
||||
"1833ee04459feb2ca4ae690d5f31269ad488c69e5fe903a42b532c677c4a8170",
|
||||
"4adb4ff2dc72bbf1f6da19fc109008a25013c837cf712016972fad015b19513f",
|
||||
"c4eabae1be3cf657bc1855ee05e69de9f059cb7a059227168b80b89761cbc4e0",
|
||||
"368f4e0027fd223fdb69b6ec6e1c06d1f027a611b1ed38eeb32493eb2878bb35",
|
||||
"703e26b4f8bc0fa57f99d815dbb75b086012acc24fc557befa310f5aa08d1898",
|
||||
"50d94fc2d8580c682b071a542f8b1e31a200b0508bab95a33bef0855df281d63",
|
||||
"6e1534f56fc9e937e06237c8ba4b5662bcacc4e1a3cfab9c16d89390bec4fca3",
|
||||
"a5e93aef8e820cbc7ab7b6205f854b87aed4b48c5f6b30fbbeba5c99e40dcf3f",
|
||||
"1989034e56b8f606c724f45a12ce84a11841621aaf7182a1f6564380b9c4276b",
|
||||
"19fefd7f39c96d2ff76f87f7627ae79145bc971d8ab23205005939a5a913bc2f",
|
||||
"6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93",
|
||||
"a3b0ce5d70d0db22885706b2b1f144c6864a7e4828acff3f8f01ca6b3f54ad15",
|
||||
"aef0d6b212827f3ba1de6189613e6d4824f181f567b1205273c16895fdaf0b23",
|
||||
"826e9f895b81ab41a4522268b249e68d02ca81608def562a493cee35ffc5c759",
|
||||
"460c25e682fda7832b52d1f22d3d22b3176d972f60dcdc3212ed8c92ef85065c",
|
||||
"e1055729d51e037b3c14e8c56e2c79c22183385d94aadb32e5dc88092cd0fef4",
|
||||
"27f211f4542fd89d673cfad15b6d838cc5d525615aae8695ed1dcebc39b2dadb",
|
||||
"eab0e756d32b80bcd464f3d844b8040303075a13eabc3599a762c9ac7ab91f4f",
|
||||
"32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245",
|
||||
"63fe6318dc58583cfe16810f86dd09e18bfd76aabc24a0081ce2856f330504ed",
|
||||
"00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700",
|
||||
"7fa56f5d6962ab1e3cd424e758c3002b8665f7b0d8dcee9fe9e288d7751ac194",
|
||||
"22aa81510ee63fe2b16cae16e0921f78e9ba9882e2868e7e63ad6d08ae9b5954",
|
||||
"175f568d77fb0cb7400f0ddd8aed1738cd797532b314ef053a1669d4dba7433a",
|
||||
"6c535d95a8659b234d5a0805034f5f0a67e3c0ceffcc459f61f680fe944424bf",
|
||||
"9579444852221038dcba34512257b66a1c6e5bdb4339b6794826d4024b3e4ce9",
|
||||
"58c741aa630c2da35a56a77c1d05381908bd10504fdd2d8b43f725efa6d23196",
|
||||
"b8e6bf46e109314616fe24e6c7e265791a5f2f4ec95ae8aa15d7107ad250dc63",
|
||||
"84dee6e676e5bb67b4ad4e042cf70cbd8681155db535942fcc6a0533858a7240",
|
||||
"387519cafd325668ecffe59577f37238638da4cf2d985b82f932fc81d33da1e8",
|
||||
"b9e76546ba06456ed301d9e52bc49fa48e70a6bf2282be7a1ae72947612023dc",
|
||||
"4d62dd5e6ac55ae2405940f59f6f030a994ec2b3ecc5556c8dc542cce20e46dd",
|
||||
"9c612f8b770f0e3fd35cdac2bc57fcee8561e560504ea25c8b9eff8e03512b3e",
|
||||
"3eeb3de14ec5c48c6c4c9ff80908c4186170eabb74b2a6705a7db9f9922cd61e",
|
||||
"51d7f1b736d1958fa56f113e82a27987a3aca4f0e6d237fa8fc369cc1608c5c0",
|
||||
"c2622c916d9b90e10a81b2ba67b19bdfc5d6be26c25756d1f990d3785ce1361b",
|
||||
"b111d517452f9ef015e16d60ae623a6b66af63024eec941b0653bfee0dd667d4",
|
||||
"d897efcd971f8e5eae08c86b7da66f89b30e761a4a86ac41d907425d15b630fe",
|
||||
"9dea27855974a08fceb48c40fab8432c1a8e3a53a1da22a1ad568595d6010649",
|
||||
"47b630bbcdfa88b1c85f84aa3b68fe6c0102b651ba5d9a23cbd2d07b4f6eecc1",
|
||||
"eb0dc09a61fdfc0df5db1f20c7fc7d83f00c690580fea2e5bac8f99c13f65065",
|
||||
"5c0775b1ae0a5140da9599aa9cd1c5beea55c2d55a5d681808525eb5fce37b32",
|
||||
"b474e6999980aa9e8c9dd6e3720fb03136bfa05aba5fab1634dc0bd8767d412f",
|
||||
"759f7abf05ca710bf2c8da7ad7a9a7df6d0c85db7b2217da524e94e3627b2fbd",
|
||||
"060e7c6ed0dbeb9c8cdc61445ee38b9b08d899d6b617e28064b0916e243ddddb",
|
||||
"f728d9e6e7048358e70930f5ca64b097770d989ccd86854fe618eda9c8a38106",
|
||||
"bf2376e17ba4ec269d10fcc996a4746b451152be9031fa48e74553dde5526bce",
|
||||
"b99dbca0184a32ce55904cb267b22e434823c97f418f36daf5d2dff0dd7b5c27",
|
||||
"c7dccba4fe4426a7b1ea239a5637ba40fab9862c8c86b3330fe65e9f667435f6",
|
||||
"ad46db12ee250a108756ab4f0f3007b04d7e699f45eac3ab696077296219d207",
|
||||
"59fbee7369df7713dbbfa9bbdb0892c62eba929232615c6ff2787da384cb770f",
|
||||
"d7f0e3917c466f1e2233e9624fbd6d4bd1392dbcfcaf3574f457569d496cb731",
|
||||
"e9e4276490374a0daf7759fd5f475deff6ffb9b0fc5fa98c902b5f4b2fe3bba2",
|
||||
"6f35047caf7432fc0ab54a28fed6c82e7b58230bf98302bf18350ff71e10430a",
|
||||
"fdd5e8f6ae0db817be0b71da20498c1806968d8a6459559c249f322fa73464a7",
|
||||
"883fea4c071fda4406d2b66be21cb1edaf45a3e058050d6201ecf1d3596bbc39",
|
||||
"a1808558470389142e297d4729e081ab8bdff1ab50d0ebe22ffa78958f7a6ab7",
|
||||
"330fb1431ff9d8c250706bbcdc016d5495a3f744e047a408173e92ae7ee42dac",
|
||||
"a4cb51f4618cfcd16b2d3171c466179bed8e197c43b8598823b04de266cef110",
|
||||
"9c163c7351f8832b08b56cbb2e095960d1c5060dd6b0e461e813f0f07459119e",
|
||||
"0a722ca20e1ccff0adfdc8c2abb097957f0e0bf32db18c4281f031756d50eb8d",
|
||||
"5cad82c898ee66013711945d687f7d9549f645a0118467dae2f5e274c598d6ff",
|
||||
"03b593ef3d95102b54bdff77728131a7c3bdfe9007b0b92cd7c4ad4a0021de25",
|
||||
"d0debf9fb12def81f43d7c69429bb784812ac1e4d2d53a202db6aac7ea4b466c",
|
||||
"60d53675f07dee9e7d77910efa44682d87cb532313ba66b8f4449d649172296b",
|
||||
"d3ab33199eb48c6f785072b4a66a8e57814e35d31375cca8c3ceeecc171f30ba",
|
||||
"772bd267dffbff318d1a89f257c3371410111a8b89571dbbefa77af6bfa179f3",
|
||||
"11b9a89404dbf3034e7e1886ba9dc4c6d376f239a118271bd2ec567a889850ce",
|
||||
"0497384b57b43c107a778870462901bf68e0e8583b32e2816563543c059784a4",
|
||||
"5d9ba2c5ee0e86e2c4477b145eb301f2df06063a19f4c4ab9042bd347188ec8e",
|
||||
"5683ffc7ff8a732565135aad56cdff94ebacd9a616d1313aea8ad48a446bfe99",
|
||||
"3004d45a0ab6352c61a62586a57c50f11591416c29db1143367a4f0623b491ca",
|
||||
"b24e32ee9a1c18f2771b53345ed8dbc55b59cbe958e5a165dc01704c3aaa6196",
|
||||
"0a2df905acd5b5be3214a84cb2d4f61b0efb4d9bf05739d51112252504959688",
|
||||
"95361a2b42a26c22bac3b6b6ba4c5cac4d36906eb0cfb98268681c45a301c518",
|
||||
"b07d216f2f0422ec0252dd81a6513b8d0b0c7ef85291fbf5a85ef23f8df78fa7",
|
||||
"064de2497ce621aee2a5b4b926a08b1ca01bce9da85b0c714e883e119375140c",
|
||||
"5a8e581f16a012e24d2a640152ad562058cb065e1df28e907c1bfa82c150c8ba",
|
||||
"a36bdc7952e973b31cb32d4ce3ce21447db66c3149c1b7a3d2450f77f9c7e8f9",
|
||||
"e03cfe011d81424bb60a12e9eb0cb0c9c688c34712c3794c0752e0718b369ef2",
|
||||
"2edbcea694d164629854a52583458fd6d965b161e3c48b57d3aff01940558884",
|
||||
"b9003833fabff271d0782e030be61b7ec38ce7d45a1b9a869fbdb34b9e2d2000",
|
||||
"4379e76bfa76a80b8db9ea759211d90bb3e67b2202f8880cc4f5ffe2065061ad",
|
||||
"76c71aae3a491f1d9eec47cba17e229cda4113a0bbb6e6ae1776d7643e29cafa",
|
||||
"d307643547703537dfdef811c3dea96f1f9e84c8249e200353425924a9908cf8",
|
||||
"da0cc82154bdf4ce8bf417eaa2d2fa99aa65c96c77867d6656fccdbf8e781b18",
|
||||
"3511ad63cd9ad760780044b7c815ee55e8e00722b5de271c47ff29367653456c",
|
||||
"f9acb0b034c4c1177e985f14639f317ef0fedee7657c060b146ee790024317ec",
|
||||
"0c371f5ed95076613443e8331c4b60828ed67bcdefaa1698fb5ce9d7b3285ffb",
|
||||
"ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49",
|
||||
"053935081a69624466034446eda3374d905652ddbf8217c88708182687a33066",
|
||||
"a305cc8926861bdde5c71bbb6fd394bb4cea6ef5f5f86402b249fc5ceb0ce220",
|
||||
"03a6e50be223dbb49e282764388f6f2ca8826eae8c5a427aa82bb1b61e51d5e6",
|
||||
"a197639863cf175adc96348382a73b4a4a361c6b2e6fc1de61a14244a2f926a1",
|
||||
"3ca7ca157b5975ace02225caf99fdce43f11207c072cb4899c80a414a9c7539d",
|
||||
"02d9f5676fffc339ffe94dfab38bebe21ce117c6f1509d9922a82d454f420da2",
|
||||
"08634a74c9d14479b462389b307695815f9a189e8fb6e058b92e18bd3f537405",
|
||||
"ec7de4aa8758ba9e09a8c89d2757a1fa0e2cc61c20b757af52ae058931c1a33f",
|
||||
"2250f69694c2a43929e77e5de0f6a61ae5e37a1ee6d6a3baef1706ed9901248b",
|
||||
"a9434ee165ed01b286becfc2771ef1705d3537d051b387288898cc00d5c885be",
|
||||
"bd625f1b8c49a79f075f3ebd2d111ff625504cf2ad12442fd70d191dd2f4a562",
|
||||
"25e5c82273a271cb1a840d0060391a0bf4965cafeb029d5ab55350b418953fbb",
|
||||
"42224859763652914db53052103f0b744df79dfc4efef7e950fc0802fc3df3c5",
|
||||
"11d0b66747887ba9a6d34b23eb31287374b45b1a1b161eac54cb183c53e00ef7",
|
||||
"2544cfcb89d7c2f8d3a31ea2ed386ac5189a18f484672436580eec215f9b039c",
|
||||
"d4338b7c3306491cfdf54914d1a52b80a965685f7361311eae5f3eaff1d23a5b",
|
||||
"c43e382ee4835010b9fad18e0a6f50f1ae143b98e089b8bb974232fce4d1f295",
|
||||
"92de68b21302fa2137b1cbba7259b8ba967b535a05c6d2b0847d9f35ff3cf56a",
|
||||
"55f04590674f3648f4cdc9dc8ce32da2a282074cd0b020596ee033d12d385185",
|
||||
"2af01e0d6bd1b9fbb9e3d43157d64590fb27dcfbcabe28784a5832e17befb87b",
|
||||
"35b23cd02d2d75e55cee38fdee26bc82f1d15d3c9580800b04b0da2edb7517ea",
|
||||
"7e0c255fd3d0f9b48789a944baf19bf42c205a9c55199805eb13573b32137488",
|
||||
"ee0e01eb17fc6cb4cd2d9300d2f8945b51056514f156c6bc6d491b74496d161a",
|
||||
"cbc5ef6b01cbd1ffa2cb95a954f04c385a936c1a86e1bb9ccdf2cf0f4ebeaccb",
|
||||
"ec6e36d5c9eb874f1db4253ef02377f7cc70697cda40fbfb24ded6b5d14cce4c",
|
||||
"2779f3d9f42c7dee17f0e6bcdcf89a8f9d592d19e3b1bbd27ef1cffd1a7f98d1",
|
||||
"976713246c36db1a4364f917b98633cbe0805d46af880f6b50a505d4eb32ed47",
|
||||
"8766a54ef9a170b3860bc66fd655abb24b5fda75d7d7ff362f44442fbdeb47b9",
|
||||
"ea2e3c814d08a378f8a5b8faecb2884d05855975c5ca4b5c25e2d6f936286f14",
|
||||
"e2ccf7cf20403f3f2a4a55b328f0de3be38558a7d5f33632fdaaefc726c1c8eb",
|
||||
"07eced8b63b883cedbd8520bdb3303bf9c2b37c2c7921ca5c59f64e0f79ad2a6",
|
||||
"532d830dffe09c13e75e8b145c825718fc12b0003f61d61e9077721c7fff93cb",
|
||||
"1afe0c74e3d7784eba93a5e3fa554a6eeb01928d12739ae8ba4832786808e36d",
|
||||
"c708943ea349519dcf56b2a5c138fd9ed064ad65ddecae6394eabd87a62f1770",
|
||||
"ccaa58e37c99c85bc5e754028a718bd46485e5d3cb3345691ecab83c755d48cc",
|
||||
"5b0e8da6fdfba663038690b37d216d8345a623cc33e111afd0f738ed7792bc54",
|
||||
"f2c96c97f6419a538f84cf3fa72e2194605e1848096e6e5170cce5b76799d400",
|
||||
"aa55a479ad6934d0fd78f3dbd88515cd1ca0d7a110812e711380d59df7598935",
|
||||
"bd9eb657c25b4f6cda68871ce26259d1f9bc62420487e3224905b674a710a45a",
|
||||
"69a80567e79b6b9bc7282ad595512df0b804784616bedb623c122fad420a2635",
|
||||
"fa984bd7dbb282f07e16e7ae87b26a2a7b9b90b7246a44771f0cf5ae58018f52",
|
||||
"df173277182f3155d37b330211ba1de4a81500c02d195e964f91be774ec96708",
|
||||
"675b84fe75e216ab947c7438ee519ca7775376ddf05dadfba6278bd012e1d728",
|
||||
"91c9a5e1a9744114c6fe2d61ae4de82629eaaa0fb52f48288093c7e7e036f832",
|
||||
"24ebb6b58d0b984a965b76f82ce9eff8795cc95085a4d09dedc56949ed596ada",
|
||||
"bb1cf5250435ff475cd8b32acb23e3ee7bbe8fc38f6951704b4798513947672c",
|
||||
"922945779f93fd0b3759f1157e3d9fa20f3fd24c4b8f2bcf520cacf649af776d",
|
||||
"3d842afecd5e293f28b6627933704a3fb8ce153aa91d790ab11f6a752d44a42d",
|
||||
"e8d67c435a4a59304e1414280e952efe17be4254fca27916bf63f9f73e54aba4",
|
||||
"c1fc7771f5fa418fd3ac49221a18f19b42ccb7a663da8f04cbbf6c08c80d20b1",
|
||||
"8eee8f5a002e533e9f9ffef14c713da449c23f56f4415e7995552075a02d1d37",
|
||||
"c998a5739f04f7fff202c54962aa5782b34ecb10d6f915bdfdd7582963bf9171",
|
||||
"a536ab1f7f3c0133baadbdf472b1ac7ad4b774ed432c1989284193572788bca0",
|
||||
"c9b19ffcd43e6a5f23b3d27106ce19e4ad2df89ba1031dd4617f1b591e108965",
|
||||
"e6ee5b449c220defea6373b8a7e147cabd67c2bdb5016886bf6096a3c7435a61",
|
||||
"a619cf1a888a73211bbf32e0c438319f23e91444d45d7bc88816ed5fcb7e8fa3",
|
||||
"56a6b75373c8f7b93c53bcae86d8ffbaba9f2a1b38122054fcdb7f3bf645b727",
|
||||
"89bfe407c647eb1888871f756516bb1906254fba3132d516ce9099614e37d10c",
|
||||
"b7ed68b062de6b4a12e51fd5285c1e1e0ed0e5128cda93ab11b4150b55ed32fc",
|
||||
"4657dfe8965be8980a93072bcfb5e59a65124406db0f819215ee78ba47934b3e",
|
||||
"d61f3bc5b3eb4400efdae6169a5c17cabf3246b514361de939ce4a1a0da6ef4a",
|
||||
"58ead82fa15b550094f7f5fe4804e0fe75b779dbef2e9b20511eccd69e6d08f9",
|
||||
"fcf6fee0e959c7195dadc5f36fe5a873003b389e7033293b06057c821fcbc9c5",
|
||||
"6681268ace4748d41a4cfcc1e64006fb935bbc359782b3d9611f64d51c6752d9",
|
||||
"e76450df94f84c1c0b71677a45d75b7918f0b786113c2d038e6ab8841b99f276",
|
||||
"a44dbc9aaa357176a7d4f5c3106846ea096b66de0b50ee39aff54baab6c4bf4b",
|
||||
"281e109d2a2899bb0555cf0c3a69b24b3debd61885ca29ef39b95b632be25fe7",
|
||||
"5be6446aa8a31c11b3b453bf8dafc9b346ff328d1fa11a0fa02a1e6461f6a9b1",
|
||||
"e1ff3bfdd4e40315959b08b4fcc8245eaa514637e1d4ec2ae166b743341be1af",
|
||||
"0d6c8388dcb049b8dd4fc8d3d8c3bb93de3da90ba828e4f09c8ad0f346488a33",
|
||||
"9be0be0e64d38a29a9cec9a5c8ef5d873c2bfa5362a4b558da5ff69bc3cbb81e",
|
||||
"c48e29f04b482cc01ca1f9ef8c86ef8318c059e0e9353235162f080f26e14c11",
|
||||
"4c7f826edf647462f744b3f16d485f53c797eabdb21cc8a7bb0713283b88e629",
|
||||
"d1621db4d91b23180707b8d4eb9b599fa8ec1dfc2453793a1f83878bd4bbc9d8",
|
||||
"4b74667f89358cd582ad82b16a2d24d5bfcb89ac4b1347ee80e5674a13ba78b2",
|
||||
"83d999a148625c3d2bb819af3064c0f6a12d7da88f68b2c69221f3a746171d19",
|
||||
"b6494a74d18a2dfa3f80ced9fadae35807716fce1071e4de19e2d746b6d87606",
|
||||
"b9c411db4036219e3dfcbe28d60e550b46cce86260fcf2c65d281258e437556f",
|
||||
"2590201e2919a8aa6568c88900192aa54ef00e6c0974a5b0432f52614a841ec8",
|
||||
"c15a5a65986e7ab4134dee3ab85254da5c5d4b04e78b4f16c82837192d355185",
|
||||
"dab6c6065c439b9bafb0b0f1ff5a0c68273bce5c1959a4158ad6a70851f507b6",
|
||||
"baf27a4cc4da49913e7fdecc951fd3b971c9279959af62b02b761a043c33384c",
|
||||
"6c237d8b3b120251c38c230c06d9e48f0d3017657c5b65c8c36112eb15c52aeb",
|
||||
"f173040998481bcb2534a53433eafb8d6ea4c7b0e1fc64572830471fe43fc77d",
|
||||
"36732cc35fe56185af1b11160a393d6c73a1fe41ddf1184c10394c28ca5d627b",
|
||||
"126103bfddc8df256b6e0abfd7f3797c80dcc4ea88f7c2f87dd4104220b4d65f",
|
||||
"457e17b7ea97a845a0d1fa8feda9976596678e3a8af46dc6671d40e050ce857d",
|
||||
"1739d937dc8c0c7370aa27585938c119e25c41f6c441a5d34c6d38503e3136ef",
|
||||
"b676ded7c768d66a757aa3967b1243d90bf57afb09d1044d3219d8d424e4aea0",
|
||||
"33bd77e5394520747faae1394a4af5fa47f404389676375b6dc7be865ed81452",
|
||||
"fe7f6bc6f7338b76bbf80db402ade65953e20b2f23e66e898204b63cc42539a3",
|
||||
"4f83ef69228b3e09b0abc11ded9e6b85319c0b7fef1a044b8ee9970e38441817",
|
||||
"4523be58d395b1b196a9b8c82b038b6895cb02b683d0c253a955068dba1facd0",
|
||||
"d8f38b894b42f7008305cebf17b48925654f22b180c5861b81141f80ccf72848",
|
||||
"9c557e253213c127a86e333ff01c9f12f63091957efafd878d220a0e2cb1193e",
|
||||
"9eab64e92219ccedb15ea9b75ababaa4ae831451019394e0e3336390c3a742d8",
|
||||
"43dedbafef3748c3f9146b961c9b87a3f5cdb1ccb50b4f5890e408702a27a506",
|
||||
"17717ad4d20e2a425cda0a2195624a0a4a73c4f6975f16b1593fc87fa46f2d58",
|
||||
"ee6ea13ab9fe5c4a68eaf9b1a34fe014a66b40117c50ee2a614f4cda959b6e74",
|
||||
"4d023ce9dfd75a7f3075b8e8e084008be17a1f750c63b5de721e6ef883adc765",
|
||||
"d91191e30e00444b942c0e82cad470b32af171764c2275bee0bd99377efd4075",
|
||||
"4eb88310d6b4ed95c6d66a395b3d3cf559b85faec8f7691dafd405a92e055d6d",
|
||||
"0aeb0814c99a13df50643ca27b831a92aaae6366f54e9c276166347aa037d63a",
|
||||
"16f1a0100d4cfffbcc4230e8e0e4290cc5849c1adc64d6653fda07c031b1074b",
|
||||
"148d1366a5e4672b1321adf00321778f86a2371a4bdbe99133f28df0b3d32fa1",
|
||||
"7076f6592de184f9e912c617c46e5e83bad91d3b7f88b7b54cc73bf8ca493321",
|
||||
"dea51494fec5947d27ca659b73dd281ff5bdba3f89f5da1977a731ad0c22e725",
|
||||
"aa97e3d392b97c21327081cdb3cb674dfa8c9c663493db43799a4079555ad1b1",
|
||||
"b7dfbebf760efb2c756b3cae22baafdbbdf55abb782f85e93b4db804d5cba7e3",
|
||||
"ca696d7edb4a86be7c7d9872bd2f9a44440cf8e2de7853536cbb3a60ae89641f",
|
||||
"ff27d01cb1e56fb58580306c7ba76bb037bf211c5b573c56e4e70ca858755af0",
|
||||
"58dece9ff68d021afe76d549b9e48e6cb7b06a5c14cdf45332c8ed7321d6f211",
|
||||
"78688c1f371e7b923d95368c9298cca06c1ec0a89ea897aa181bd60091121fea",
|
||||
"9e8dd91d21e867dec464868a8d1f4a27c0e113c53e32f2bec0a7c6e25ad2e9d5",
|
||||
"6d028aa49aa1f584b3d35aee9fcee8e3c0d81108114289aa046a7969b21eb5f5",
|
||||
"2c470abbac95a49cd0ed5b3b9e628ffda1dbb03c14caba1a225a9b8bf1dc9d5f",
|
||||
"9d7af6946b320b3ba6b4d386de2b2cf3f8ac52fdcb63f3343d1a8362693a3ce5",
|
||||
"d7a4345c3ead1ea7a34bd6aae43c63cbd81941d9ba019fe972843e5ce78e3187",
|
||||
"00f471f6312ce408f7eb56592a2b6c6b5f54ac2967c77f4c1498903b598e1b16",
|
||||
"41e9e2c8398583b90204f8e35c2a4c036aeebac6d05dbdc3e7fb44a1d6bd65a2",
|
||||
"0d978064b9054c023111926050d983573dac2aff16bb8a7497fde8ad725357c0",
|
||||
"507f5bf8367c1883f115ddf9ee95f79ea693c720eb5a5a8718443a99fa308954",
|
||||
"9a090f86adf9fbdc37de2a14745e73d6e1fd096d3da0670b6795ce5ad3cfeea3",
|
||||
"f2b7c5787424c8f9cf6c4480eb99f4a3770cc06337a4f0d1b109ba849b464193",
|
||||
"6ffe93bb72d4ac788fd8be2dadf5bb4a2f14a330d530b0e68bd733c9744c6619",
|
||||
"3ebc74907d1f928f209ef210e872cac033eaf3ff89e6853286d45d91e351ef9e",
|
||||
"da56c54b5e6749d65ad038c196478794af94e4fa5a4efdc20b49981e4ec566c3",
|
||||
"266815e0c9210dfa324c6cba3573b14bee49da4209a9456f9484e5106cd408a5",
|
||||
"5b705e6cb602425c019202dd070a0c009b040ac19960eeef2d8a8fab25c1efe5",
|
||||
"9ec7a778167afb1d30c4833de9322da0c08ba71a69e1911d5578d3144bb56437",
|
||||
"e771af0b05c8e95fcdf6feb3500544d2fb1ccd384788e9f490bb3ee28e8ed66f",
|
||||
"40b9c85fffeafc1cadf8c30a4e5c88660ff6e4971a0dc723d5ab674b5e61b451",
|
||||
"efe5d120df0cc290fa748727fb45ac487caad346d4f2293ab069e8f01fc51981",
|
||||
"408f636bd26fcc5f29889033b447cb2411f60ab1b8a5fc8cb3842dab758fdeb5",
|
||||
"d8a2c33f2e2ff3a9d4ff2a5593f3d5a59e9167fa5ded063d0e49891776611e0c",
|
||||
"02a11d1545114ab63c29958093c91b9f88618e56fee037b9d2fabcff32f62ea9",
|
||||
"1bbd7fdf68eaf5c19446c3aaf63b39dd4a8e33548bc96f6bd239a4124d8f229e",
|
||||
"726a1e261cc6474674e8285e3951b3bb139be9a773d1acf49dc868db861a1c11",
|
||||
"167e7fe01a76b6bec9d2a9b196b18c72e150e985fbeb46ee651869e7b4032785",
|
||||
"c88f94f0a391b9aaa1ffefd645253b1a968b0a422a876ea48920a95d45c33f47",
|
||||
"cd169bd8fbd5179e2a8d498ffc31d3ae0e40825ff2b8a85ea359c4455a107ca8",
|
||||
"fd38f135ef675eac5e93d5b2a738c41777c250188031caf1dcf07b1687a1fe49",
|
||||
"6b4c612991132cf4c6c390dceaae75041b9954ba4f9c465aca70beb350815a57",
|
||||
"8fec426247845bdd26f36ae4f737508c15dbec07d43ce18f8c136ab9e35ac212",
|
||||
"af551accea482000bdccb34bd3c521558e1f353773a3caed83a147921c369ea1",
|
||||
"a664a4973cd23e9f3b35a62429f7671aba2c2ae68c03313913b5c2d77269d906",
|
||||
"18f54af1e10c5bb7a35468b0f62b295d12347903c9f95738d065c84bef1402ef",
|
||||
"be39043cc12efbddfee564d95da751a71df6c139e2def45c431cadeb4a573ca3",
|
||||
"01ddee289b1a2e90874ca3428a7a414764a6cad1abfaa985c201e7aada16d38c",
|
||||
"da25cf7b457bddb6b7bc8e1b0146c0fa85373807d6efdac955199fd01fd53c1f",
|
||||
"ec380784d96b93d404166a6bf1a778227a94a02bdf499f94b5f48f61f5b1350f",
|
||||
"6538925ebfb661f418d8c7d074bee2e8afd778701dd89070c2da936d571e55c3",
|
||||
"9edd72eb23222c969379d90d60ec82891b7c827188bb28510a863f59cb697b0a",
|
||||
"09222857afceb23c66c99fc93d8e5ebda6d7aad901eb38af73c508f117685012",
|
||||
"744ecc9a119a92da88b1f448b4030cdbc2fec5c37ea06ebdd026e742b002af7f",
|
||||
"f531a8672baa2415b271e866dbe11fb08640f6c0e5d98f918bd0308e7169b5b7",
|
||||
"44dc1c2db9c3fbd7bee9257eceb52be3cf8c40baf7b63f46e56b58a131c74f0b",
|
||||
"89e14be49ed0073da83b678279cd29ba5ad86cf000b6a3d1a4c3dc4aa4fdd02c",
|
||||
"8fb140b4e8ddef97ce4b821d247278a1a4353362623f64021484b372f948000c",
|
||||
"72f9755501e1a4464f7277d86120f67e7f7ec3a84ef6813cc7606bf5e0870ff3",
|
||||
"3d99feac152027ede63326aa4f43d4ca88e4cd27296b96fe18c55d496a8f6340",
|
||||
"2540d50aeb9be889c3bd050c9cc849b57b156a2759b48084a83db59aa9056eb4",
|
||||
"b66be78da89991544a05c3a2b63da1d15eefe8e9a1bb6a4369f8616865bd6b7c",
|
||||
"2f5de0003db84ecd5449128350c66c7fb63e9d02b250d84af84f463e2f9bcef1",
|
||||
"2045369fc115b138d1438f98d3c29916986c9fde6b8203f7ff8699f0faee1c93",
|
||||
"ae1008d23930b776c18092f6eab41e4b09fcf3f03f3641b1b4e6ee3aa166d760",
|
||||
"1ec454734dcbf6fe54901ce25c0c7c6bca5edd89443416761fadc321d38df139",
|
||||
"b2d670de53b27691c0c3400225b65c35a26d06093bcc41f48ffc71e0907f9d4a",
|
||||
"ac3f6afe17593f61810513dac9a1e544e87b9ce91b27d37b88ec58fbaa9014aa",
|
||||
"d1f3c71639ae3bba17ffc6c8deb1fdb3a56506b3492213d033528cc291523704",
|
||||
"6b4a29bbd43d1d0eeead384f512dbb591ce9407d27dba48ad54b00d9d2e1972b",
|
||||
"84de08882b6e36705cf6592ee58e632dd6e092dd61c13192fc80cbbc0cbc82cc",
|
||||
"d3d74124ddfb5bdc61b8f18d17c3335bbb4f8c71182a35ee27314a49a4eb7b1d",
|
||||
"a008def15796fba9a0d6fab04e8fd57089285d9fd505da5a83fe8aad57a3564d",
|
||||
"eb7246eb8e26b0c48dd4f9c2a822a0f4d5c84138937195090932b61a2d756051",
|
||||
"683211bd155c7b764e4b99ba263a151d81209be7a566a2bb1971dc1bbd3b715e",
|
||||
"5468bceeb74ce35cb4173dcc9974bddac9e894a74bf3d44f9ca8b7554605c9ed",
|
||||
"78ce6faa72264387284e647ba6938995735ec8c7d5c5a65737e55130f026307d",
|
||||
"2754fc862d6bc0b7c3971046612d942563d181c187a391e180ed6b00f80e7e5b",
|
||||
"f1725586a402c06aec818d1478a45aaa0dc16c7a9c4869d97c350336d16f8e43",
|
||||
"6a359852238dc902aed19fbbf6a055f9abf21c1ca8915d1c4e27f50df2f290d9",
|
||||
"9e4954853fca260cecf983f098e5204c68b2bdfebde91f1f7b25c10b566d50f8",
|
||||
"3356de61b39647931ce8b2140b2bab837e0810c0ef515bbe92de0248040b8bdd",
|
||||
"3e294d2fd339bb16a5403a86e3664947dd408c4d87a0066524f8a573ae53ca8e",
|
||||
"21335073401a310cc9179fe3a77e9666710cfdf630dfd840f972c183a244b1ad",
|
||||
"987096ef8a2853fea1a31b0ed5276503da291536f167bbf7f3f991c9f05d6d7f",
|
||||
"7a78fbfec68c2b3ab6084f1f808321ba3b5ea47502c41115902013e648e76288",
|
||||
"c12a2bcb002fd74b4d342f9b942c24c44cc46d5ed39201245a8b6f4162e7efce",
|
||||
"8867bed93e89c93d0d8ac98b2443c5554799edb9190346946b12e03f13664450",
|
||||
"9b61cd02adac4b18fbcc06237e7469b07e276faf6ec4ecb34b030c2e385892a0",
|
||||
"0463223adf38df9a22a7fb07999a638fdd42d8437573e0bf19c43e013b14d673",
|
||||
"9989500413fb756d8437912cc32be0730dbe1bfc6b5d2eef759e1456c239f905",
|
||||
"17e2889fba01021d048a13fd0ba108ad31c38326295460c21e69c43fa8fbe515",
|
||||
"6c6c253fe26a5b2abf440124e35dcaa39e891cd28274431ba49da5c11d89747d",
|
||||
"9d065f84c0cba7b0ef86f5d2d155e6ce01178a8a33e194f9999b7497b1b2201b",
|
||||
"5ffb8e1b6b629c0e34a013f9298ebb0759b98a3d24029916321d5eb4255b6735",
|
||||
"3fc5f8553abd753ac47967c4c468cfd08e8cb9dee71b79e12d5adab205bc04d3",
|
||||
"ff82c8b53aa53a9705200690b91c572e2e4918f1a88de5d923ac06fa4560fa19",
|
||||
"4d4ab737e2fbb5af0fd590b4b7e8c6fe76d3a02a9791ef7fdacf601f9e50fad8",
|
||||
"5eca50a04afaefe55659fb74810b42654e2268c1acca6e53801b9862db74a83a",
|
||||
"d700fc10d457eeae4f02eb04d715a054837e68a2e2d010971382c5e1016dc99e",
|
||||
"af321973db865bb33fbc50a4de67fc0e6808d812c6e4dfd9cbc2fd50275b1dfd",
|
||||
"bbf923aa9246065f88c40c7d9bf61cccc0ff3fcff065a8cb2ff4cfbb62088f1e",
|
||||
"268b948b5aab4bab0e5430ee49e3cff11776cf183df93b32159f9670ed541495",
|
||||
"3d2e51508699f98f0f2bdbe7a45b673c687fe6420f466dc296d90b908d51d594",
|
||||
"4d4fb5ff0afb8c04e6c6e03f51281b664576f985e5bc34a3a7ee310a1e821f47",
|
||||
"9b12847f3d28bf8850ebc03f8d495a1ae8f9a2c86dbda295c90556619a3ee831",
|
||||
"733c5427f55ceba01a0f6607ab0fd11832bbb27d7db17b570e7eb7b68a081d9a",
|
||||
"4bc7982c4ee4078b2ada5340ae673f18d3b6a664b1f97e8d6799e6074cb5c39d",
|
||||
"afa0f26dbf3e674630d1cd6499e86c14f316cd4f78c6ab73bb85b00aa9c50a57",
|
||||
"c301f13372c8f0d9bc8186d874fa45fa33aede13e66f4187a3bd22ee41c95b2a",
|
||||
"548a29f145187fc97689f8ae67944627723c315c163b0dbb88842e50c681d7ca",
|
||||
"d0a1ffb8761b974cec4a3be8cbcb2e96a7090dcf465ffeac839aa4ca20c9a59e",
|
||||
"faaf47af27e3de06e83f346fc6ccea0aabfc7520d82ffe90c48dfcd740c69caa",
|
||||
"3eacaa768326d7dce80f6ee17ada199bebe7eb3c1a60b39b14e0a58bbac66fe4",
|
||||
"7f5237e9f77a22c4a89624c7ac31cae797d8ac4144b02493890d54fee7399bcd",
|
||||
"d84517802a434757c56ae8642bffb4d26e5ade0712053750215680f5896e579b",
|
||||
"bdb96ad31ac6af123c7683c55775ee2138da0f8f011e3994d56a27270e692575",
|
||||
"aab1b0caf13b9bd26a62cf8b3b20f9bfaa0e56f3ec42196a00fedf432e07d739",
|
||||
"c230edd34ca5c8318bf4592ac056cde37519d395c0904c37ea1c650b8ad4a712",
|
||||
"ce41c1698a8c042218bc586f0b9ec8d5bffa3dcbcea09bd59db9d0d92c3fc0b4",
|
||||
"b9a537523bba2fcdae857d90d8a760de4f2139c9f90d986f747ce7d0ec0d173d",
|
||||
"1a6e0aeff1dba7ba121fbeb33bf3162901495df3fcb4e4a40423e1c10edf0dca",
|
||||
"21b419102da8fc0ba90484aec934bf55b7abcf75eedb39124e8d75e491f41a5e",
|
||||
"2183e94758481d0f124fbd93c56ccaa45e7e545ceeb8d52848f98253f497b975",
|
||||
"e3f98bfb9cbeb7563a139983602e50f616cb7ebb06c3295b8ee377328f051206",
|
||||
"b5b9b84d1723994d06013606227fb5b91f9de8820b04cf848d1dccc23d054f39",
|
||||
"07adfda9c5adc80881bb2a5220f6e3181e0c043b90fa115c4f183464022968e6",
|
||||
"facdaf1ce758bdf04cdf1a1fa32a3564a608d4abc2481a286ffc178f86953ef0",
|
||||
"efc83f01c8fb309df2c8866b8c7924cc8b6f0580afdde1d6e16e2b6107c2862c",
|
||||
"52b4a076bcbbbdc3a1aefa3735816cf74993b1b8db202b01c883c58be7fad8bd",
|
||||
"c6f7077f1699d50cf92a9652bfebffac05fc6842b9ee391089d959b8ad5d48fd",
|
||||
"e7424ad457e512fdf4764a56bf6d428a06a13a1006af1fb8e0fe32f6d03265c7",
|
||||
"27797bd4e5ee52db0a197668c92b9a3e7e237e1f9fa73a10c38d731c294cfc9a",
|
||||
"7bdef7bdebb8721f77927d0e77c66059360fa62371fdf15f3add93923a613229",
|
||||
"3335d373e6c1b5bc669b4b1220c08728ea8ce622e5a7cfeeb4c0001d91ded1de",
|
||||
"645681b9d067b1a362c4bee8ddff987d2466d49905c26cb8fec5e6fb73af5c84",
|
||||
"06b7819d7f1c7f5472118266ed7bca8785dceae09e36ea3a4af665c6d1d8327c",
|
||||
"7a6b8c7de171955c214ded7e35cc782cd6dddfd141abb1929c632f69348e6f49",
|
||||
"eb882b0bb659bf72235020a0b884c4a7d817e0af3903715736b146188b1d0868",
|
||||
"2ae6c71323a225ecfa8cf655600ebbe12b1019ff36bf02726d82d095aab29729",
|
||||
"c2f85a06279a7bfa7f2477a3cee907990231a13d17b54524738504bd12e0c86c",
|
||||
"f1b911af1c7a56073e3b83ba7eaa681467040e0fbbdd265445aa80e65c274c22",
|
||||
"a54c2ae6ec6ac06b4d7b45c483eab86ac226b8ecfa99163ef7cc000da9b40895",
|
||||
"bbc73cae41502ddad7a4112586dcaf4422810d60aa4b57c637ccd1a746b07844",
|
||||
"218238431393959d6c8617a3bd899303a96609b44a644e973891038a7de8622d",
|
||||
"59cacbd83ad5c54ad91dacf51a49c06e0bef730ac0e7c235a6f6fa29b9230f02",
|
||||
"ba80990666ef0b6f4ba5059347beb13242921e54669e680064ca755256a1e3a6",
|
||||
"031db544f3158556508b321db59afd62c5bb1592021e5dfd9ff87dca0ad27d8c",
|
||||
"ee85604f8ec6e4e24f8eaf2a624d042ebd431dae448fe11779adcfb6bb78575e",
|
||||
"266ee74062e8dae0aeddfcd0f72725107598efaa80c1a7176d6ee6dd302bce4c",
|
||||
"b83a28b7e4e5d20bd960c5faeb6625f95529166b8bdb045d42634a2f35919450",
|
||||
"dbe0605a9c73172bad7523a327b236d55ea4b634e80e78a9013db791f8fd5b2c",
|
||||
"1e53e900c3bbc5ead295215efe27b2c8d5fbd15fb3dd810da3063674cb7213b2",
|
||||
"832a2b3cef4b1754c4a7572964a44db64d19edf627ec45179b519d0a5eae8199",
|
||||
"4c800257a588a82849d049817c2bdaad984b25a45ad9f6dad66e47d3b47e3b2f",
|
||||
"3743244390be53473a7e3b3b8d04dce83f6c9514b81a997fb3b123c072ef9f78",
|
||||
"f96c3d76497074c4c83a7b3823380e77dc73d5a9494fd2e053e4a1453e17824b",
|
||||
"d04ecf33a303a59852fdb681ed8b412201ba85d8d2199aec73cb62681d62aa90",
|
||||
"0cca6201658d5d98239c1511ef402562ff7d72446fb201a8d1857c39e369c9fa",
|
||||
"61066504617ee79387021e18c89fb79d1ddbc3e7bff19cf2298f40466f8715e9",
|
||||
"7adb520c3ac7cb6dc8253508df0ce1d975da49fefda9b5c956744a049d230ace",
|
||||
"7579076d9aff0a4cfdefa7e2045f2486c7e5d8bc63bfc6b45397233e1bbfcb19",
|
||||
"9ec078ef9ca31e1bdbb97175dde1cb00bf9f7225e6f622ccc8d367302e220497",
|
||||
"93e174736c4719f80627854aca8b67efd0b59558c8ece267a8eccbbd2e7c5535",
|
||||
"e62f419a0e16607b96ff10ecb00249af7d4b69c7d121e4b04130c61cc998c32e",
|
||||
"171ddd43dab1af0d1fb14029287152a4c89296890e0607cf5e7ba73c73fdf1a5",
|
||||
"604e96e099936a104883958b040b47672e0f048c98ac793f37ffe4c720279eb2",
|
||||
"7726c437ccf791f6ded97dbac1846e62019e5fbd24f42e9db2f640f231c3c09a",
|
||||
"1bf9f239dca1636149bc2f3fc334077ae959ea9607cacf945ef8f8bb227dc5e1",
|
||||
"fcd818454002a6c47a980393f0549ac6e629d28d5688114bb60d831b5c1832a7",
|
||||
"56172b53f730750b40e63c501b16068dd96a245e7f0551675c0fec9817ee96e0",
|
||||
"260d3a820b7f8de20f4972725999b1af88b0cc5554ca38f9681c8d657e043cc3",
|
||||
"9ba8c688f091ca48de2b0f9bc998e3bc36a0092149f9201767da592849777f1c",
|
||||
"61594d714aa94fe551f604123578c4a6592145f4228ad8601224b1b89ce401b0",
|
||||
"416ca193aa5448b8cca1f09642807765cc0ee299609f972df0614cfb8ea2f2b1",
|
||||
"9b6d95b76a01191a4c778185681ed7f3bced2fffa8e41516ec78240b213285f5",
|
||||
"ee0304bae0d4679bb34347ce3b1b80482262b9812bd0c0d5e19a5e2445043b75",
|
||||
"8027a1877f39e603dafc63279e004b4ed9df861d18ce81d9c43c7d7135da8f65",
|
||||
"42b409ff9b261a985227b1ab92707e706777ac14de24654d7e05f0501b37e003",
|
||||
"99097983b74c70800b182abc6f64046ab70407e9cabcd6cf570a38ada9ef75d5",
|
||||
"de8ca7a6b3f7314e91921d4dc5e915fb7bc2bd32129ea6966322effa48050c4c",
|
||||
"dcb302978215f54f33c3d2d7157ef69fd5058cf488fc43dd75c32b5dcaf47e7a",
|
||||
"7c765d407d3a9d5ea117cb8b8699628560787fc084a0c76afaa449bfbd121d84",
|
||||
"06639a386c9c1014217622ccbcf40908c4f1a0c33e23f8d6d68f4abf655f8f71",
|
||||
"59f1b5faf29904fe94a6a042e2d82d80d68fc16ad7651eba90a8de39f63f8fe8",
|
||||
"174398550d1468a41b98a09f496c38d3694feadef0f0073fd557610384bafb10",
|
||||
"00d52016bd7e4aae8bf8eaa23f42276b649fe557483b5d7684702633dd0fd944",
|
||||
"9a39bf837c868d61ed8cce6a4c7a0eb96f5e5bcc082ad6afdd5496cb614a23fb",
|
||||
"74ffc51cc30150cf79b6cb316d3a15cf332ab29a38fec9eb484ab1551d6d1856",
|
||||
"97c70a44366a6535c145b333f973ea86dfdc2d7a99da618c40c64705ad98e322",
|
||||
"6c6e3e05e1c9d2aae0ed2431544aea411771dd9d81017539af0fd818b2389f28",
|
||||
"23a2cf63ec81e65561acafc655898d2fd0ef190084653fa452413f75e5a3d5bc",
|
||||
"f1f9b0996d4ff1bf75e79e4cc8577c89eb633e68415c7faf74cf17a07bf80bd8",
|
||||
"e3aefda887252a72cee3578d33b2dcd90e9fe53b8bed6347ef5e26f74211adbb",
|
||||
"6b4ec98f02e647e01440b473bbd92a7fae21e01b6aa6c65e32db94a36092272e",
|
||||
"623ed218de81311783656783d6ce690b521a89c4dc09f28962e5bfd4fa549249",
|
||||
"9ce71f1506ccf4b99f234af49bd6202be883a80f95a155c6e9a1c36fd7e780c7",
|
||||
"139fcc6bb304b2879974c59cda61d86d7816ad4ac0f38ee7a724df488060e65d",
|
||||
"0e8c41eb946657188ea6f2aac36c25e393fff4d4149a83679220d66595ff0faa",
|
||||
"59ffbe1fc829decf90655438bd2df3a7b746ef4a04634d4ee9e280bb6ce5f14e",
|
||||
"e4f695f05bb05b231255ccce3d471b8d79c64a65bccc014662d27f0f7e921092",
|
||||
"39cc53c9e3f7d4980b21bea5ebc8a5b9cdf7fa6539430b5a826e8ad527168656",
|
||||
"685fb49563864326e78df461468795b7e47849a27e713281cd8bb75c0547936d",
|
||||
"05e4649832dfb8d1bfa81ea7cbf1c92c4f1cd5052bfc8d5465ba744aa6fa5eb8",
|
||||
"e5cece49ae3fc2c81f50c8e7a93a5fb1e1585380c467e4822234b64a94add617",
|
||||
"dda028cd1b806b4d494cc7f2789b6c2bd7e3c28ff6a267d03acc5ac6e69a05e0",
|
||||
"046c436b2a525059867b40c81e469b6d83001442fc65312c87a7ce7abeb022ff",
|
||||
"676ffea2ec31426a906d7795d7ebae2ba5e61f0b9fa815995b4a299dd085d510",
|
||||
"15b5cf6cdf4fd1c02f28bcce0f197cafae4c8c7c66a3e2e23af9fe610875315e",
|
||||
"c0fb5367cfcb803c5383f98e26524bed9176e6211588f53ec63fe6079cbfd3df",
|
||||
"7ab00f596b0286b77f78af567ee1be2536feee41daee67bd872f1480b7aa65b9",
|
||||
"e8d66519e43b1214ac68f9f2bdbc4386d41ac66b20c5a260b9b04102784074e9",
|
||||
"e6618db6961dc7b91478e0fa78c4c1b6699009981526693bd5e273972550860c",
|
||||
"b1e1185884a6d14bbfce3899cb53e8183adde642f264d0ff4f1745371e06134c",
|
||||
"cae5b7ea348afefc4c102bb7b125c4928f114739a27b877c6bcfbe5a79280384",
|
||||
"ecbe372132a9323b813eeb48f8dfcedaeca00e2887af181b063c6cfa13ed8ea1",
|
||||
"52387c6b99cc42aac51916b08b7b51d2baddfc19f2ba08d82a48432849dbdfb2",
|
||||
"3c39a7b53dec9ac85acf08b267637a9841e6df7b7b0f5e2ac56a8cf107de37da",
|
||||
"f8e6c64342f1e052480630e27e1016dce35fc3a614e60434fef4aa2503328ca9",
|
||||
"fd0266485777bd73e97c7c37f520c83c82e362fe4c25a6be93f3380083d4646b",
|
||||
"433e80c14ff7b8e16e179ccec35f55833df7dd5a5a063d23117b4b01b6f97170",
|
||||
"b7c6f6915cfa9a62fff6a1f02604de88c23c6c6c6d1b8f62c7cc10749f307e81",
|
||||
"ddf03aca85ade039e6742d5bef3df352df199d0d31e22b9858e7eda85cb3bbbe",
|
||||
"d36e8083fa7b36daee646cb8b3f99feaa3d89e5a396508741f003e21ac0b6bec",
|
||||
"ec79b568bdea63ca6091f5b84b0c639c10a0919e175fa09a4de3154f82906f25",
|
||||
"8cd2d0f8310f7009e94f50231870756cb39ba68f37506044910e2f71482b1788",
|
||||
"0eef96197f5c6be3859b6817e6a5736685856c416e29a2925bd5a15b2a57c8b1",
|
||||
"04918dfc36c93e7db6cc0d60f37e1522f1c36b64d3f4b424c532d7c595febbc5",
|
||||
"c8383d81dd24406745b68409be40d6721c301029464067fcc50a25ddf9139549",
|
||||
"3b3a42d34cf0a1402d18d536c9d2ac2eb1c6019a9153be57084c8165d192e325",
|
||||
"da18e9860040f3bf493876fc16b1a912ae5a6f6fa8d5159c3de2b8233a0d9851",
|
||||
"e3fc673fc5f99cc554d0ff47756795647d25cb6e6658f912d114ae6429d35d35",
|
||||
"3aa5817273c3b2f94f491840e0472f049d0f10009e23de63006166bca9b36ea3",
|
||||
"bbb5dda0e15567979f0543407bdc2033d6f0bbb30f72512a981cfdb2f09e2747",
|
||||
"1096f6be0a4d7f0ecc2df4ed2c8683f143efc81eeba3ece6daadd2fca74c7ecc",
|
||||
"d76726da1b64e8679d8b6e66facf551ba96f2612de5a171fac818ee85ce3e5fe",
|
||||
"27487c9600b16b24a1bfb0519cfe4a5d1ad84959e3cce5d6d7a99d48660a1f78",
|
||||
"5d3ab876c206a37ad3b094e20bfc3941df3fa21a15ac8ea76d6918473789669a",
|
||||
"6b1b8dac34ffc61d464dfeef00e4a84a604e172ef6391fb629293d6f1666148c",
|
||||
"6fb266012c3008303e54ae55140b46957e9978098401dda34f4d921a275bf8bb",
|
||||
"53a91e3a64d1f658e983ac1e4f9e0c697f8f33e01d8debe439f4c1a92113f592",
|
||||
"5082984480f3b27891840a2037512739149678efc2ac981ca8cd016d02304efd",
|
||||
"7b849efa5604b58d50c419637b9873847dbf957081d526136c3a49b7357cd617",
|
||||
"f53b9d91a8cd177fb4a1cf081a1b6d58759a381ef120a7c5a18c0e70cae80983",
|
||||
"cfd7df62799a22e384a4ab5da8c4026c875b119d0f47c2716b20cdac9cc1f1a6",
|
||||
"d83b5ef189df7e884627294b752969547814c3cfe38995cf207c040e03bbe7a4",
|
||||
"96f652249b0946e1575d78a8bc7450123c8e64f1c56f6b2f93bc23fb249ed85a",
|
||||
"d60bdad03468f5f8c85b1b10db977e310a5aafab33750dfadb37488b02bfc8d7",
|
||||
"9839f160d893daae661c84168e07f46f0e1e9746feb8439a6d76738b4ad32eaa",
|
||||
"453a656903a031395d450f318211a6ec54cd79049a851f92cd6702c65ff5f5bd",
|
||||
"1634b87b5fcfd4a6c4ff2f2de17450ccce46f9abe0b02a71876c596ec165bfed",
|
||||
"24480686b56234a240fd9827209b584847f3d4f9657f0d9a97aec5320a264acb",
|
||||
"f4d1866e8599563c52ceeedf11c28b8567e465c6e9a91df92add535d57f02ab0",
|
||||
"805b34f708837dfb3e7f05815ac5760564628b58d5a0ce839ccbb6ef3620fac3",
|
||||
"659a74f6cfbc7c252c58d93452b9d9575e36c464aa6544c6375227c9166a6ed9",
|
||||
"75d737c3472471029c44876b330d2284288a42779b591a2ed4daa1c6c07efaf7",
|
||||
"dac1d8c5a9fe94f224e095b52577c33c2cc2b8f3a2d6ad9cbd46845af8c987f0",
|
||||
"be7358c4fe50148cccafc02ea205d80145e253889aa3958daafa8637047c840e",
|
||||
"30e8cbf1427c137fa60674a639431c19a9d6f4c07fd2959df83158e674fccbaa",
|
||||
"7f573f55d875ce8edc528edf822949fd2ab9f9c65d914a40225663b0a697be07",
|
||||
"781a1527055f74c1f70230f10384609b34548f8ab6a0a6caa74025827f9fdae5",
|
||||
"d82a91e1013170b10ca7fa0ec800fd0dc6e9335b70c303dadba603fc36802b5f",
|
||||
"a4237e420cdb0b3231d171fe879bcae37a2db7abf2f12a337b975337618c3ac2",
|
||||
"7ff4d89f90845ac4d7a50a259163798e0f446e61d4c943cc89637beff567ad02",
|
||||
"48dbb5e717a6221d64fd13ba12794bc28e5067ac1d7632ee9437d533772750df",
|
||||
"efba340bd479176486e5a2281c97ac4a90fdcf86ec9c13a78c3182ab877cd19b",
|
||||
"1021c8921548fa89abb4cc7e8668a3a8dcebae0a4c323ffeaf570438832d6993",
|
||||
"c67cd3e1a83daa56cff16f635db2fdb9ed9619300298d4701a58e68e84098345",
|
||||
"4ad6fa2d16e2a9b576c863b4cf7404a70d4dc320c0c447d10ad6ff58993eacc8",
|
||||
"e568a76a4f8836be296d405eb41034260d55e2361e4b2ef88350a4003bbd5f9b",
|
||||
"ebdee92945ef05283be0ac3de25787c81a6a58a10f568f9c6b61d9dd513adbad",
|
||||
"6e8f3edfa28bfc8057d735794f76b697bcf18fb894a5a37a132693ebda31a464",
|
||||
"576d23dc3db2056d208849462fee358cf9f0f3310a2c63cb6c267a4b9f5848f9",
|
||||
"18905d0a5d623ab81a98ba98c582bd5f57f2506c6b808905fc599d5a0b229b08",
|
||||
"a9046cc9175dc5a45fb93a2c890f9a8b18c707fa6d695771aab9300081d3e21a",
|
||||
"7a69e5f62fcc20e81beea7461a945e6531f8c7944200d0b3cb4cc63556c44106",
|
||||
"fd0266485777bd73e97c7c37f520c83c82e362fe4c25a6be93f3380083d4646b",
|
||||
"4b29db7a76f3b4fbc0a4fffc092e41c14f1a1a975a462d87e82827af03719cb2",
|
||||
"df1a6cb6c95a5bdd2a69e4fa921061da950fc0bb0b3529d04ca75e0c11f871df",
|
||||
"08bfc00b7f72e015f45c326f486bec16e4d5236b70e44543f1c5e86a8e21c76a",
|
||||
"1e908fbc1d131c17a87f32069f53f64f45c75f91a2f6d43f8aa6410974da5562",
|
||||
"b3a737d014a7e75f44b0f5afbd752f9bcc2abe54f60dbbebc3681b6e16611967",
|
||||
"d3052ca3e3d523b1ec80671eb1bba0517a2f522e195778dc83dd03a8d84a170e",
|
||||
"b98ded4ceaea20790dbcb3c31400692009d34c7f9927c286835a99b7481a5c22",
|
||||
"9e1e498420bc7c35f3e3a78d20045b4c8343986dae48739759bccb2a27e88c53",
|
||||
"141d2053cb29535ad45aa9e865cdec492524f0ec0066496b98b7099daab5d658",
|
||||
"8722c3843c85ddd6162a5cb506e1cb4d6ab0cafb966034f426e55a2ef89a345e",
|
||||
"52dfd21724329920c5c95f5361464e468584136d30030eb29247a7fe6c2c6e36",
|
||||
"d82a91e1013170b10ca7fa0ec800fd0dc6e9335b70c303dadba603fc36802b5f",
|
||||
"6bbb7d71eaa2544215a877e136cd7f490f4625eb56459a0da856cc8296d5df30",
|
||||
"1ebb28301aa1a48248d3723a0ea434bb7d4612ec920fa749583e3f41ce25849f",
|
||||
"00000001505e7e48927046e9bbaa728b1f3b511227e2200c578d6e6bb0c77eb9",
|
||||
"a01b5ba26421374250442e0d23f96e6a4bce429e0175cd0769ad8c585dd5a892",
|
||||
"26d6a946675e603f8de4bf6f9cef442037b70c7eee170ff06ed7673fc34c98f1",
|
||||
"1dd7992ea0ecbda7480ceed35748c3655691133c8c78af947fd24299db8f481f",
|
||||
"cdee943cbb19c51ab847a66d5d774373aa9f63d287246bb59b0827fa5e637400",
|
||||
"3d03c53608415b1d718c7786ee10bdb4e67bced32207e32880ee9e44301a19ec",
|
||||
"170dc4045d6c06275b40bd39f68ca31dbb962094e9763ee460f8341bd40bebca",
|
||||
"db1abbff170320730e5ace672ad7217161b8935afc1a896ae2fecf903c159932",
|
||||
"b0ac2c26eabdb0e0a9b0d10fd1458ca73c575b19d65e13f0e7484cbee84038b3",
|
||||
"c1e7fc21b4f9c199e6086e095639f0f16a4e4884544547ce8a653ed7b5b6c4a7",
|
||||
"813c2662366a12f6337b951c048552fd3c4894e403cab701634dcd803786dc09",
|
||||
"fd0bcf8cd1aee83fe75e6c0fdfc543845e5bc3f50d26d2d5e5c6d3fa521f98c0",
|
||||
"45fd1955f590da87c1fd2edb99d17accf235ec3ebf0afe1d3306ade42693c6e9",
|
||||
"27938497226683c701e2843c6db580e2f0e25f5a198f4c3397e3a0a27764215d",
|
||||
"2321edfd415f9558605b4d7a7083c52624e8922ae86bb2ae359fbf829724111a",
|
||||
"0461fcbecc4c3374439932d6b8f11269ccdb7cc973ad7a50ae362db135a474dd",
|
||||
"1f8e182bf72d61cb78dcd6b96dd3be8b874b8881da6630757b6508970f67230c",
|
||||
"c6603b0f1ccfec625d9c08b753e4f774eaf7d1cf2769223125b5fd4da728019e",
|
||||
"296842eaaed9be5ae0668da09fe48aac0521c4af859ad547d93145e5ac34c17e",
|
||||
"88f8707a45e825a13ed383332abe6e2f104ab44d877918be22550083a2b59e60",
|
||||
"27a20b41a66b35d442302a50ca1baad72c2ed844c8d1224c9f6d50a12752084e",
|
||||
"280e847ef0c82a2a7c4e877c91cd7567474c1431b815d27bbc1017e147d9d77c",
|
||||
"ad9d42203fd2480ea2e5c4c64593a027708aebe2b02aa60bd7b1d666daa5b08d",
|
||||
"bb0174ae21a6cac1a0a9c8b4ac6ebfda56ce51605c315b1824970bc275f7239a",
|
||||
"edb470271297ac5a61f277f3cd14de54c67eb5ccd20ef0d9df29be18685bb004",
|
||||
"9609b093450dd7e0afb389619acdaf2e6a0d6817c93552f3911e05b50ae73e3d",
|
||||
"3e33fd7124f174fc535151937f8718634dd9d856143d4cefb5a10ddaf2f615c0",
|
||||
"463555bb4b0f80fd1376fae628fabfaf7e5e31cd2741d80aa4d225c926bc287e",
|
||||
"916cb5ff07d3b51cef7f6b6b7f5479b1001b401c0e82558ee1a22504c7d507c9",
|
||||
"cc5f259f036683798e4a52071dbb97238702ffb6f0c85af6d273c8ddbe5c0afb",
|
||||
"2ad91f1dca2dcd5fc89e7208d1e5059f0bac0870d63fc3bac21c7a9388fa18fd",
|
||||
"8bf629b3d519a0f8a8390137a445c0eb2f5f2b4a8ed71151de898051e8006f13",
|
||||
"94215f42a96335c87fcb9e881a0bbb62b9a795519e109cf5f9d2ef617681f622",
|
||||
"bcbf9644d3f475d00eb9c6e467385ce16d4546c1a24222ccfa542bf776eaba95",
|
||||
"ba5115c37b0f911e530ed6c487ccbd9b737da33fd4b88a9f590860378c06af62",
|
||||
"609f186ca023d658c0fe019570472f59565c8be1dc163b1541fac9d90aa4e8af",
|
||||
"4e5622b575cdbb4d5ded093e48c68cd3f724fad547142f0c1b0aec2f2b2a0b2e",
|
||||
"4df7b43b3a4db4b99e3dbad6bd0f84226726efd63ae7e027f91acbd91b4dba48",
|
||||
"bcbf9644d3f475d00eb9c6e467385ce16d4546c1a24222ccfa542bf776eaba95",
|
||||
"3a06add309fd8419ea4d4e475e9c0dff5909c635d9769bf0728232f3a0683a84",
|
||||
"d2384c4ac623eb9f15ae4cb32ee7a9b03e0202802d52f2395f2ee8f6433151aa",
|
||||
"1d4cc828b657da8c7a101e8657365459b9dc74139bed5d35bd8295b00be2a1ae",
|
||||
"76fcec0e0638351f1d0e0dc4ebaf6dd3d67404126d664547674070f3175273d9",
|
||||
"6707c39e6c53ef945c5df29af78667dc941ed80094994bd264fd806a6e0a3230",
|
||||
"80482e60178c2ce996da6d67577f56a2b2c47ccb1c84c81f2b7960637cb71b78",
|
||||
"147784df719c09fad62bff0493a60b4f5dbbe8579e73f00d207350e8ffdfd65f",
|
||||
"afc0295d2c6e0a1820c214c07312070a4070d52083163d7fe410fa02bf85d9d2",
|
||||
"6a5e3cc17279cbdf051c06d96e3f843cdb296f351d8ca35a6a190c0ab90dbf9a",
|
||||
"3b7fc823611f1aeaea63ee3bf69b25b8aa16ec6e81d1afc39026808fe194354f",
|
||||
"d96fe9c5478d1bb36e9ec40cc678b0bf7ff67e017922a151f925640a8884f291",
|
||||
"1d80e5588de010d137a67c42b03717595f5f510e73e42cfc48f31bae91844d59",
|
||||
"06dde95f0268ce40128bf73ca6e85567b8567688ea52f24dcd5734e77c50f2d9",
|
||||
"f683e87035f7ad4f44e0b98cfbd9537e16455a92cd38cefc4cb31db7557f5ef2",
|
||||
"036533caa872376946d4e4fdea4c1a0441eda38ca2d9d9417bb36006cbaabf58",
|
||||
"7cc328a08ddb2afdf9f9be77beff4c83489ff979721827d628a542f32a247c0e",
|
||||
"f240be2b684f85cc81566f2081386af81d7427ea86250c8bde6b7a8500c761ba",
|
||||
"fb61b93d864e4f0eba766bb8556f2dc0262e8e985012e29ba28508dd52067d98",
|
||||
"0e52122d1eb95cdd8ba5f65815f7d1c9125a8c14d82989eae52ab369eea6c7e4",
|
||||
"04dcaf2552801937d1c20b69adf89646f21b53c17906271d22c7be9bcadb96c0",
|
||||
"0ee827a36e8bb0cfc483cf1872781182c4a16c58acba3ae2d7b155e0370e93b8",
|
||||
"adc14fa3ad590856dd8b80815d367f7c1e6735ad00fd98a86d002fbe9fb535e1",
|
||||
"2bc9be7569515701581d5d765422a17ee3500d8e1f4e7aa53f6be86ae6ba274d",
|
||||
"9a21569255d0a3a9e75f1de2e4c883c9be2e5615887f22b2ecf6b1813bcd587d",
|
||||
"3f3ff7adb39159c42c0aa16d53c0483bfbfad57df22a9e9e9364a306741eb2cf",
|
||||
"e9aa50decff01f2cec1ec2b2e0b34332cf9c92cafdac5a7cc0881a6d26b59854",
|
||||
"c7eda660a6bc8270530e82b4a7712acdea2e31dc0a56f8dc955ac009efd97c86",
|
||||
"787338757fc25d65cd929394d5e7713cf43638e8d259e8dcf5c73b834eb851f2",
|
||||
"7e8ffe414a53ce18a85536eda74d3cbda0da6a98d4fe6c514948002472178c95",
|
||||
"6a04ab98d9e4774ad806e302dddeb63bea16b5cb5f223ee77478e861bb583eb3",
|
||||
"0c371f5ed95076613443e8331c4b60828ed67bcdefaa1698fb5ce9d7b3285ffb",
|
||||
"daa41bedb68591363bf4407f687cb9789cc543ed024bb77c22d2c84d88f54153",
|
||||
"12ee03d11684a125dd87be879c28190415be3f3b1eca6b4ed743bd74ffd880e6",
|
||||
"00dfb20815a71e24572394efdfbf772e56a507921b8287201ab8937496ee8e6d",
|
||||
"94a6a78a5aebbba40bd1aaa2234810132c2d8004bb9177616c413d3c0ddf320e"
|
||||
];
|
||||
const PUBKEY_ARRAYS = generateTestPubkeyArrays();
|
||||
|
||||
// Main testing function
|
||||
async function testRelayPubkeyLimits() {
|
||||
console.log('Starting Nostr Relay Pubkey Filter Test');
|
||||
console.log(`Testing ${RELAYS.length} relays with up to ${MAX_PUBKEYS} pubkeys`);
|
||||
console.log(`Incrementing by ${STEP_SIZE} pubkeys per test, requesting ${EVENT_LIMIT} events per test\n`);
|
||||
|
||||
const results = {};
|
||||
|
||||
// Initialize results for each relay
|
||||
for (const relayUrl of RELAYS) {
|
||||
results[relayUrl] = {
|
||||
maxPubkeys: 0,
|
||||
failures: 0,
|
||||
lastSuccess: 0
|
||||
};
|
||||
}
|
||||
|
||||
// Test each pubkey array size
|
||||
for (const pubkeyArray of PUBKEY_ARRAYS) {
|
||||
const pubkeyCount = pubkeyArray.length;
|
||||
console.log(`\nTesting with ${pubkeyCount} pubkeys...`);
|
||||
|
||||
// Test each relay with this pubkey count
|
||||
for (const relayUrl of RELAYS) {
|
||||
if (results[relayUrl].failures >= 2) {
|
||||
console.log(` ${relayUrl}: Skipping (already failed 2 times)`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const success = await testRelayWithPubkeys(relayUrl, pubkeyArray);
|
||||
if (success) {
|
||||
results[relayUrl].maxPubkeys = pubkeyCount;
|
||||
results[relayUrl].lastSuccess = pubkeyCount;
|
||||
results[relayUrl].failures = 0; // Reset failures on success
|
||||
console.log(` ${relayUrl}: ✓ Success`);
|
||||
} else {
|
||||
results[relayUrl].failures++;
|
||||
console.log(` ${relayUrl}: ✗ Failed (${results[relayUrl].failures}/2)`);
|
||||
}
|
||||
} catch (error) {
|
||||
results[relayUrl].failures++;
|
||||
console.log(` ${relayUrl}: ✗ Error (${results[relayUrl].failures}/2): ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Print final results
|
||||
console.log('\n=== FINAL RESULTS ===');
|
||||
for (const relayUrl of RELAYS) {
|
||||
const result = results[relayUrl];
|
||||
console.log(`${relayUrl}: ${result.maxPubkeys} pubkeys (last success: ${result.lastSuccess})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test a single relay with a specific pubkey array
|
||||
async function testRelayWithPubkeys(relayUrl, pubkeys) {
|
||||
return new Promise((resolve) => {
|
||||
const ws = new WebSocket(relayUrl);
|
||||
let receivedEOSE = false;
|
||||
let subscriptionId = 'test_' + Math.random().toString(36).substr(2, 9);
|
||||
let timeoutId;
|
||||
|
||||
const cleanup = () => {
|
||||
clearTimeout(timeoutId);
|
||||
ws.close();
|
||||
};
|
||||
|
||||
ws.on('open', () => {
|
||||
console.log(`Connected to ${relayUrl}`);
|
||||
// Send subscription request
|
||||
const filter = {
|
||||
kinds: [EVENT_KIND],
|
||||
authors: pubkeys,
|
||||
limit: EVENT_LIMIT
|
||||
};
|
||||
const subscriptionMessage = ['REQ', subscriptionId, filter];
|
||||
const messageString = JSON.stringify(subscriptionMessage);
|
||||
const messageSize = Buffer.byteLength(messageString, 'utf8');
|
||||
console.log(`Sending request with ${pubkeys.length} pubkeys (${messageSize} bytes)`);
|
||||
ws.send(messageString);
|
||||
|
||||
// Set timeout for EOSE response
|
||||
timeoutId = setTimeout(() => {
|
||||
if (!receivedEOSE) {
|
||||
console.log('Timeout waiting for EOSE');
|
||||
cleanup();
|
||||
resolve(false);
|
||||
}
|
||||
}, 10000); // 10 second timeout
|
||||
});
|
||||
|
||||
ws.on('message', (data) => {
|
||||
try {
|
||||
const message = JSON.parse(data.toString());
|
||||
if (message[0] === 'EVENT' && message[1] === subscriptionId) {
|
||||
// Skip event content, just log that we received an event
|
||||
console.log(`Received EVENT for subscription ${subscriptionId}`);
|
||||
} else if (message[0] === 'EOSE' && message[1] === subscriptionId) {
|
||||
console.log(`Received EOSE: ${JSON.stringify(message)}`);
|
||||
receivedEOSE = true;
|
||||
cleanup();
|
||||
resolve(true);
|
||||
} else {
|
||||
console.log(`Received other message: ${JSON.stringify(message)}`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Received non-JSON: ${data.toString()}`);
|
||||
}
|
||||
});
|
||||
|
||||
ws.on('error', (error) => {
|
||||
console.log(`WebSocket error: ${error.message}`);
|
||||
cleanup();
|
||||
resolve(false);
|
||||
});
|
||||
|
||||
// Overall connection timeout
|
||||
setTimeout(() => {
|
||||
if (!receivedEOSE) {
|
||||
cleanup();
|
||||
resolve(false);
|
||||
}
|
||||
}, 15000); // 15 second total timeout
|
||||
});
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testRelayPubkeyLimits().catch(console.error);
|
||||
Reference in New Issue
Block a user