Compare commits

..
8 Commits
63 changed files with 17881 additions and 799 deletions
+3 -2
View File
@@ -449,7 +449,8 @@ async function setupAutomaticRelayConnection(showSections = false) {
const relayInfo = await fetchRelayInfo(httpUrl);
const fetchedPubkey = relayInfo && relayInfo.pubkey ? relayInfo.pubkey : null;
relayPubkey = fetchedPubkey || '4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa';
// Fallback must match the actual running relay pubkey when NIP-11 doesn't expose pubkey.
relayPubkey = fetchedPubkey || 'e9aa50decff01f2cec1ec2b2e0b34332cf9c92cafdac5a7cc0881a6d26b59854';
// Keep relay metadata (including version) even when pubkey is missing/fallback
relayInfoData = {
@@ -466,7 +467,7 @@ async function setupAutomaticRelayConnection(showSections = false) {
}
} catch (error) {
console.log('⚠️ Could not fetch relay info, using fallback pubkey:', error.message);
relayPubkey = '4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa';
relayPubkey = 'e9aa50decff01f2cec1ec2b2e0b34332cf9c92cafdac5a7cc0881a6d26b59854';
}
// Set up subscription to receive admin API responses
+186
View File
@@ -0,0 +1,186 @@
# Plan: Eliminate g_db from the Main Thread
## Problem Statement
All SQLite calls go through `db_ops.c`, but `db_active_connection()` falls back to `g_db` when `g_thread_db` is NULL. Since the main thread never sets `g_thread_db`, every `db_*` call from lws-main executes synchronous SQLite on the main thread. The thread pool workers have their own connections and work correctly — the problem is the **fallback path**.
Current perf data shows lws-main at **67.6% avg CPU**, dominated by SQLite symbols (`sqlite3BtreeTableMoveto`, `sqlite3VdbeExec`, `pcache1Fetch`).
## Goal
Remove `g_db` as a runtime connection used by the main thread. After this change:
- The main thread has **no SQLite connection** and cannot execute synchronous queries
- All DB work routes through thread pool workers (which have their own connections)
- `g_db` is only used during startup/shutdown (before/after the event loop)
- Any accidental `db_*` call from lws-main returns an error instead of silently blocking
## Architecture After Change
```
lws-main thread:
- WebSocket protocol handling
- Message parsing/routing
- Completion queue draining
- NO SQLite access during event loop
db-read-0 thread:
- REQ queries
- COUNT queries
- Config cache miss reads
- Auth rule checks
- Monitoring/stats queries
db-write thread:
- EVENT inserts
- Subscription logging
- Config updates
- Auth rule modifications
event-worker thread:
- Signature validation
- Duplicate check
- Store via db-write
```
## Categorized g_db Call Sites
### Category A: Startup-only — keep using g_db
These run before the event loop starts. They are fine.
| Function | File | Purpose |
|----------|------|---------|
| `db_init()` | db_ops.c:27 | Open database |
| `db_exec_sql()` for PRAGMAs | main.c:854-882 | WAL, mmap, cache setup |
| `db_table_exists()` | main.c:750 | Schema check |
| `db_get_schema_version_dup()` | main.c:754 | Migration check |
| `db_exec_sql()` for schema | main.c:842 | Create tables |
| `populate_all_config_values_atomic()` | config.c:4290 | First-time config |
| `populate_default_config_values()` | config.c:1657 | Default config |
| `add_pubkeys_to_config_table()` | config.c:1778 | Pubkey storage |
| `apply_cli_overrides_atomic()` | config.c:4211 | CLI overrides |
| `db_store_relay_private_key_hex()` | config.c:495 | Key storage |
| `db_populate_event_tags_from_existing()` | main.c:1156 | Tag migration |
| `cleanup_all_subscriptions_on_startup()` | subscriptions.c:1092 | Orphan cleanup |
### Category B: Shutdown-only — keep using g_db
| Function | File | Purpose |
|----------|------|---------|
| `db_exec_sql()` WAL checkpoint | main.c:900 | Clean shutdown |
| `db_close()` | main.c:904 | Close connection |
### Category C: Runtime hot path — must move off main thread
These are called during the event loop from lws-main context.
| Function | File | Called from | Fix strategy |
|----------|------|------------|--------------|
| `db_get_config_value_dup()` | db_ops.c:777 | config cache miss | Already cached with 5s TTL; pre-warm cache at startup so misses are rare |
| `store_event()` for kind 14/1059 | main.c:983 | websockets.c sync path | Route through async event worker |
| `store_event_post_actions()` | main.c | completion handler | Already runs on main; its DB calls need routing |
| `db_log_subscription_created()` | db_ops.c:145 | subscriptions.c | Fire-and-forget via write queue |
| `db_log_subscription_closed()` | db_ops.c:165 | subscriptions.c | Fire-and-forget via write queue |
| `db_log_subscription_disconnected()` | db_ops.c:193 | subscriptions.c | Fire-and-forget via write queue |
| `db_update_subscription_events_sent()` | db_ops.c:225 | subscriptions.c | Fire-and-forget via write queue |
| `db_cleanup_orphaned_subscriptions()` | db_ops.c:244 | subscriptions.c | Move to startup only |
| `generate_and_post_status_event()` | websockets.c:3408 | periodic timer | Submit to write worker |
| `ip_ban_cleanup()` / `ip_ban_log_stats()` | websockets.c:3196 | periodic timer | Submit to write worker |
| `db_get_total_event_count_ll()` | db_ops.c:589 | api.c monitoring | Submit to read worker |
| `db_get_event_count_since()` | db_ops.c:606 | api.c monitoring | Submit to read worker |
| `db_get_event_kind_distribution_rows()` | db_ops.c:625 | api.c monitoring | Submit to read worker |
| `db_get_top_pubkeys_rows()` | db_ops.c:663 | api.c monitoring | Submit to read worker |
| `db_get_subscription_details_rows()` | db_ops.c:697 | api.c monitoring | Submit to read worker |
| `db_get_all_config_rows()` | db_ops.c:745 | api.c config query | Submit to read worker |
| `db_execute_readonly_query_json()` | db_ops.c:442 | api.c SQL query | Submit to read worker |
| `db_event_id_exists()` | db_ops.c:1041 | main.c event check | Already moved to event worker |
| `db_retrieve_event_by_id()` | db_ops.c:1056 | main.c | Submit to read worker |
| `db_get_event_pubkey()` | db_ops.c:262 | NIP-09 deletion | Submit to read worker |
| `db_delete_event_by_id()` | db_ops.c:285 | NIP-09 deletion | Submit to write worker |
| `db_delete_older_replaceable_events()` | db_ops.c:305 | store_event_core | Already on write worker |
| `db_store_config_event()` | db_ops.c:888 | config.c | Submit to write worker |
| `db_add_auth_rule()` | db_ops.c:1226 | config.c admin | Submit to write worker |
| `db_remove_auth_rule()` | db_ops.c:1242 | config.c admin | Submit to write worker |
| `db_delete_wot_whitelist_rules()` | db_ops.c:1258 | config.c WoT | Submit to write worker |
| `db_count_wot_whitelist_rules()` | db_ops.c:1266 | config.c | Cache or read worker |
| `db_store_event_tags_cjson()` | db_ops.c:1140 | main.c post-actions | Already on write worker path |
| `db_get_config_row_count()` | db_ops.c:1121 | config.c diagnostics | Cache or read worker |
| `db_set_config_value_full()` | db_ops.c:796 | config.c | Submit to write worker |
| `db_update_config_value_only()` | db_ops.c:821 | config.c | Submit to write worker |
| `db_upsert_config_value()` | db_ops.c:838 | api.c | Submit to write worker |
| `db_exec_sql()` for transactions | config.c | admin events | Submit to write worker |
| `db_count_with_sql()` | db_ops.c:415 | config.c admin | Submit to read worker |
| `is_config_table_ready()` | config.c:4595 | config.c hybrid | Cache result at startup |
| `generate_config_event_from_table()` | config.c:4933 | config.c | Cache or read worker |
### Category D: Auth rule checks — already use separate connections
These functions in `db_ops.c` open their own temporary read-only connections:
| Function | Line | Notes |
|----------|------|-------|
| `db_is_pubkey_blacklisted()` | 347 | Opens own connection |
| `db_is_hash_blacklisted()` | 362 | Opens own connection |
| `db_is_pubkey_whitelisted()` | 377 | Opens own connection |
| `db_count_active_whitelist_rules()` | 392 | Opens own connection |
These are already safe — they don't use `g_db`. No change needed.
## Implementation Strategy
### Phase 1: Pre-warm caches and eliminate cache-miss DB reads
1. Pre-warm the config cache at startup by loading all config values into the in-memory cache before the event loop starts
2. Pre-warm the NIP-11 cache at startup
3. Pre-warm the auth rules fast cache at startup
4. This eliminates the most frequent cache-miss `db_get_config_value_dup()` calls
### Phase 2: Route subscription logging through write worker
1. Add a `THREAD_POOL_JOB_FIRE_AND_FORGET` job type to thread_pool
2. Create async wrappers: `db_log_subscription_created_async()`, etc.
3. These submit SQL to the write worker queue and return immediately
4. No completion callback needed — fire and forget
### Phase 3: Route special-kind EVENT store through async worker
1. Remove the `event_is_async_eligible()` exclusion for kinds 14, 1059, 23456
2. For kind 23456: process admin command in completion handler on main thread after store
3. For kind 14/1059: process NIP-17 DM in completion handler after store
### Phase 4: Route periodic timer DB work through workers
1. `generate_and_post_status_event()` — submit event creation to write worker
2. `ip_ban_cleanup()` / `ip_ban_log_stats()` — submit to write worker
3. Monitoring queries — submit to read worker with completion callback
### Phase 5: Null out g_db before event loop
1. After startup is complete and thread pool is initialized, set `g_db = NULL`
2. Change `db_active_connection()` to assert/warn if both `g_thread_db` and `g_db` are NULL
3. Any accidental main-thread DB call will now fail loudly instead of silently blocking
4. Restore `g_db` briefly for shutdown checkpoint
## Implementation Order
1. Phase 1 — lowest risk, immediate benefit from eliminating cache misses
2. Phase 2 — straightforward fire-and-forget pattern
3. Phase 3 — moderate complexity, needs careful completion handler design
4. Phase 4 — moderate complexity, periodic tasks need async patterns
5. Phase 5 — the final enforcement step, only safe after phases 1-4
## Risk Assessment
- **Phase 1**: Very low risk — just pre-warming existing caches
- **Phase 2**: Low risk — subscription logging is non-critical, fire-and-forget is safe
- **Phase 3**: Medium risk — admin/DM event processing has complex state; needs careful testing
- **Phase 4**: Medium risk — periodic tasks have side effects that need to complete
- **Phase 5**: High risk if done prematurely — must verify ALL runtime paths are covered first
## Expected Impact
After all phases:
- lws-main CPU should drop from ~67% to near 0% SQLite overhead
- All SQLite work happens on db-read and db-write threads
- Main thread only does WebSocket I/O, JSON parsing, and completion queue draining
- Thread model becomes: lws-main = pure I/O, workers = all DB
+194
View File
@@ -0,0 +1,194 @@
# Main Thread CPU Offload Plan
## Problem Statement
The main `c_relay` thread consumes ~56% CPU while the DB worker threads (`db-read-1..4`, `db-write`) sit near 0%. All protocol handling, JSON parsing, event validation, subscription matching, and message queueing happens synchronously inside `nostr_relay_callback()` on the main libwebsockets event-loop thread.
## Current Architecture
```mermaid
flowchart TD
LWS[lws_service - main thread] --> CB[nostr_relay_callback]
CB --> PARSE[JSON parse - cJSON_Parse]
CB --> VALIDATE[Signature verify - nostr_validate_unified_request]
CB --> STORE[store_event - builds payload]
STORE --> SYNC_WRITE[thread_pool_execute_store_event_sync]
SYNC_WRITE --> |blocks main thread| DB_WRITE[db-write thread]
DB_WRITE --> |signal| SYNC_WRITE
CB --> REQ[handle_req_message - builds SQL]
REQ --> SYNC_READ[thread_pool_execute_req_sync]
SYNC_READ --> |blocks main thread| DB_READ[db-read-N thread]
DB_READ --> |signal| SYNC_READ
CB --> BROADCAST[broadcast_event_to_subscriptions]
BROADCAST --> QUEUE[queue_message per subscriber]
CB --> WRITEABLE[LWS_CALLBACK_SERVER_WRITEABLE]
WRITEABLE --> DRAIN[process_message_queue - lws_write]
```
### Why the main thread is hot
| Work item | Where | Cost |
|-----------|-------|------|
| JSON parsing | `cJSON_Parse` in `LWS_CALLBACK_RECEIVE` | Medium - per message |
| Signature verification | `nostr_validate_unified_request` - ed25519 crypto | **High** - per EVENT |
| SQL query building | `handle_req_message` filter-to-SQL loop | Low-Medium |
| Sync DB wait | `thread_pool_execute_*_sync` - pthread_cond_wait | Blocks but yields CPU |
| Result iteration + expiration check | Row loop in `handle_req_message` with `cJSON_Parse` per row | Medium-High for large result sets |
| Subscription matching | `broadcast_event_to_subscriptions` - filter matching | Medium - scales with subscriber count |
| Message serialization + queueing | `snprintf` + `queue_message_take_ownership` per subscriber | Medium |
| Config lookups | `get_config_int/bool` called repeatedly in hot paths | Low but frequent |
### Key constraint: libwebsockets is single-threaded
libwebsockets requires that **all `lws_write`, `lws_callback_on_writable`, and `lws_close_reason` calls happen from the service thread** (the thread running `lws_service`). This means we cannot directly write to WebSocket connections from worker threads. However, we **can** do computation on worker threads and post results back to the main thread for I/O.
## Offload Strategy
### Phase 1: Async EVENT Processing (Highest Impact)
Convert EVENT handling from synchronous to async. Currently the main thread does: parse → validate → store → broadcast, all blocking. Instead:
1. **Main thread**: Parse JSON (fast), extract event ID for dedup check, then submit a job to a new "event processing" worker thread
2. **Worker thread**: Signature verification (expensive crypto), store_event (already goes to DB thread), prepare broadcast payload
3. **Main thread callback**: Receive result via `wake_loop_cb` + `lws_cancel_service`, send OK response and broadcast to subscribers
```mermaid
flowchart TD
LWS[lws_service - main thread] --> RECEIVE[LWS_CALLBACK_RECEIVE]
RECEIVE --> PARSE[JSON parse + dedup check]
PARSE --> SUBMIT[Submit to event-worker queue]
SUBMIT --> LWS
WORKER[event-worker thread] --> VERIFY[Signature verification]
VERIFY --> STORE_DB[store_event via DB pool]
STORE_DB --> PREP[Prepare broadcast payload]
PREP --> RESULT_Q[Push result to completion queue]
RESULT_Q --> WAKE[lws_cancel_service]
LWS2[lws_service wakes] --> POLL[Poll completion queue]
POLL --> OK[Send OK response via queue_message]
POLL --> BCAST[broadcast_event_to_subscriptions]
```
**What this offloads**: ed25519 signature verification (~the most expensive per-event operation), event classification, tag serialization, and the synchronous DB store wait.
**What stays on main thread**: JSON parse (needed to extract event ID for dedup), OK response writing, broadcast fan-out (requires lws access).
### Phase 2: Async REQ Query Execution (Medium Impact)
Convert REQ handling from sync to async:
1. **Main thread**: Parse filters, build SQL, create subscription, submit query job
2. **DB reader thread**: Execute query (already happens, but currently blocks main thread via `_sync`)
3. **Main thread callback**: Iterate results, queue EVENT messages, send EOSE
This is simpler than Phase 1 because the thread pool already supports async submission via `thread_pool_submit_read` with a `result_cb`. The `_sync` wrappers just add a condvar wait on top. We need to:
- Use `thread_pool_submit_read` directly instead of `thread_pool_execute_req_sync`
- Store pending REQ context (sub_id, wsi, pss) so the callback can complete the work
- In the result callback, push results to a completion queue and call `lws_cancel_service`
- On the main thread, drain the completion queue and send EVENT + EOSE messages
### Phase 3: Reduce Per-Row Overhead in REQ Results (Low-Medium Impact)
Currently each row from a REQ query gets `cJSON_Parse` just to check NIP-40 expiration. This is wasteful:
- Option A: Add an `expiration` column to the events table so expiration filtering can be done in SQL
- Option B: Store expiration timestamp in a fast-parse format (extract during INSERT, store as integer column)
- Option C: Use string search on the raw JSON for the expiration tag instead of full parse
### Phase 4: Config Value Caching (Low Impact, Easy Win)
`get_config_int` and `get_config_bool` are called on every message in hot paths. These do SQLite queries. Cache config values in memory with a TTL or invalidation signal, so the main loop only refreshes them periodically (already done for `debug_level` every 60s — extend to all hot-path config values).
## Implementation Priority
| Phase | Impact | Risk | Complexity |
|-------|--------|------|------------|
| Phase 1: Async EVENT | **High** - removes crypto from main thread | Medium - async state management | Medium-High |
| Phase 2: Async REQ | **Medium** - unblocks main thread during queries | Low - infrastructure exists | Medium |
| Phase 3: Expiration optimization | **Low-Medium** - reduces per-row parse cost | Low | Low |
| Phase 4: Config caching | **Low** - reduces DB round-trips | Very Low | Low |
## Detailed Design: Phase 1 (Async EVENT Processing)
### New Components
#### Completion Queue (`src/completion_queue.h/.c`)
A thread-safe FIFO queue for posting results from worker threads back to the main thread:
```c
typedef struct {
int type; // COMPLETION_TYPE_EVENT_RESULT, COMPLETION_TYPE_REQ_RESULT, etc.
void* data; // Type-specific result data
struct lws* wsi; // Target WebSocket connection
void* pss; // Per-session data
} completion_item_t;
int completion_queue_init(void);
int completion_queue_push(completion_item_t* item);
completion_item_t* completion_queue_pop(void); // Non-blocking
void completion_queue_shutdown(void);
```
#### Event Worker Thread
A dedicated pthread that processes EVENT validation/storage:
```c
typedef struct {
cJSON* event; // Parsed event JSON - ownership transferred
cJSON* full_message; // Full message JSON for context
struct lws* wsi;
void* pss;
char sub_id[64]; // For response routing
} event_work_item_t;
```
#### Main Loop Integration
Add a completion queue drain step to the main event loop. After `lws_service` returns (either from timeout or `lws_cancel_service` wake), check the completion queue:
```c
while (g_server_running && !g_shutdown_flag) {
int result = lws_service(ws_context, 1000);
// NEW: Drain completion queue
completion_item_t* item;
while ((item = completion_queue_pop()) != NULL) {
process_completion(item); // Send OK, broadcast, etc.
free(item);
}
// ... existing periodic checks ...
}
```
### Changes to Existing Code
1. **`nostr_relay_callback` EVENT path**: After JSON parse and dedup check, instead of calling `nostr_validate_unified_request` + `store_event` + `broadcast_event_to_subscriptions` synchronously, submit an `event_work_item_t` to the event worker queue and return 0 immediately.
2. **`store_event`**: No changes needed — it already uses `thread_pool_execute_store_event_sync` which will run on the DB writer thread. The event worker thread will call it.
3. **`broadcast_event_to_subscriptions`**: No changes needed — it will be called from the main thread when processing the completion item, which is correct since it calls `queue_message_take_ownership` (requires lws thread).
### Thread Safety Considerations
- The `cJSON* event` object must be fully owned by the worker thread during processing. The main thread must not access it after submission.
- The `wsi` and `pss` pointers could become invalid if the client disconnects while the event is being processed. The completion handler must validate that the connection is still alive before sending the OK response.
- A generation counter or epoch on `pss` can detect stale references.
## What Cannot Be Offloaded
- **`lws_write` / `queue_message`**: Must happen on the lws service thread
- **`lws_callback_on_writable`**: Must happen on the lws service thread
- **`lws_close_reason`**: Must happen on the lws service thread
- **Subscription list iteration for broadcast**: Accesses `lws_wsi_user` which is lws-internal
These are fundamental libwebsockets constraints. The pattern is always: do computation off-thread, post result to completion queue, wake main thread, do I/O on main thread.
## Expected Impact
With Phase 1 alone, the main thread would no longer perform:
- ed25519 signature verification (~100-500μs per event depending on CPU)
- Synchronous DB store wait (~50-200μs per event)
- Event classification, tag serialization, JSON serialization for storage
This should reduce main-thread CPU by roughly 30-50% for EVENT-heavy workloads, shifting that work to the event worker thread and DB threads.
+257
View File
@@ -0,0 +1,257 @@
# PostgreSQL Nostr Relay Architecture Analysis
This document summarizes common approaches and best practices for building Nostr relays backed by PostgreSQL, based on industry-standard designs and existing open-source relay implementations.
## Identified Projects
* **Nostrss**: High-performance Rust-based relay.
* **Nostrgres**: Focused on PostgreSQL integration with complex event filtering.
* **Relay-rs (Postgres branch)**: General purpose high-throughput relay.
* **Nostream** ([github.com/Cameri/nostream](https://github.com/Cameri/nostream)): Production-ready TypeScript relay backed by PostgreSQL and Redis.
* **Nostr-Relay-NestJS** ([github.com/CodyTseng/nostr-relay-nestjs](https://github.com/CodyTseng/nostr-relay-nestjs)): High-performance relay built with NestJS and PostgreSQL, utilizing Kysely for type-safe database interactions.
## Detailed Analysis
### 1. Common Schema Patterns
* **Event Storage (JSONB)**: Most PostgreSQL-backed relays store the raw event as a JSONB object in a central `events` table. This provides flexibility for the evolving Nostr spec while allowing efficient extraction of fields.
* **Tag Denormalization**: While the raw event is in JSONB, performant relays extract tags (like `p`, `e`, `t`, `d`) into a separate `tags` table with foreign key relationships to the `events` table. This avoids slow JSONB traversal during complex filter queries.
* **Author/Publisher Tracking**: A dedicated `authors` or `pubkeys` table is typically used to maintain indexing on the `pubkey` field, enabling quick lookups of user activity.
### 2. Performance Optimization
* **GIN Indexes on JSONB**: Crucial for filtering on specific event tags or custom properties stored within the event object. GIN indexes with `jsonb_path_ops` are generally preferred for equality checks.
* **Time-Series Partitioning**: Given the append-only nature of Nostr, partitioning the `events` table by time (e.g., daily or weekly chunks) is highly recommended. This significantly improves query performance for recent data and simplifies data expiration/deletion.
* **Clustering**: Clustering the `events` table by the timestamp index can reduce disk I/O, as it keeps temporally related events physically adjacent on the disk.
### 3. Schema Management Approaches
* **Migrations**: Most mature relays utilize migration tools (like `Flyway`, `Diesel migrations`, or `Golang-migrate`) to version control database schema changes. This is critical for production stability.
* **Auto-generation**: Some lightweight prototypes auto-generate schemas at startup. This is generally discouraged for production due to the risk of destructive changes, data loss, or blocking DDL operations on large tables.
## Recommendations for Building a New Relay
1. **Prioritize Denormalization**: Do not rely solely on JSONB queries for high-traffic filters. Extract indexed tags into dedicated columns or tables.
2. **Use Partitioning from Day One**: Implementing native PostgreSQL partitioning (e.g., using `pg_partman`) is much easier before the dataset grows to millions of rows.
3. **Connection Pooling**: PostgreSQL requires aggressive connection management. Use a high-performance pooler like `PgBouncer` to handle the large number of concurrent, short-lived connections common in WebSocket-based relay traffic.
4. **Asynchronous Writes**: Decouple the WebSocket ingestion thread from the database writer thread to ensure that slow database writes do not impact the relay's responsiveness.
## Nostream ([github.com/Cameri/nostream](https://github.com/Cameri/nostream))
A production-ready Nostr relay written in TypeScript (Node.js), backed by PostgreSQL 14+ and Redis. Uses Knex.js for versioned migrations.
### PostgreSQL Schema
The schema is fully normalized — no JSONB blob for the whole event. Tags are stored separately and populated by a trigger.
**`events` table** — one row per event, tags kept as a JSONB column for the trigger to process:
```sql
CREATE TABLE events (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
event_id BYTEA UNIQUE NOT NULL, -- 32-byte hash
event_pubkey BYTEA NOT NULL,
event_kind INTEGER UNSIGNED NOT NULL,
event_created_at INTEGER UNSIGNED NOT NULL,
event_content TEXT NOT NULL,
event_tags JSONB, -- raw tags array, source of truth for trigger
event_signature BYTEA NOT NULL,
remote_address TEXT,
expires_at INTEGER,
deleted_at TIMESTAMP,
event_deduplication JSONB, -- used for replaceable event conflict key
first_seen TIMESTAMP DEFAULT now()
);
-- Indexes
CREATE INDEX ON events (event_pubkey);
CREATE INDEX ON events (event_kind);
CREATE INDEX ON events (event_created_at);
CREATE INDEX ON events (expires_at);
CREATE INDEX event_tags_idx ON events USING GIN (event_tags); -- GIN on raw JSONB
-- Unique index enforcing NIP-16 replaceable event semantics
CREATE UNIQUE INDEX replaceable_events_idx ON events (event_pubkey, event_kind)
WHERE event_kind = 0 OR event_kind = 3
OR (event_kind >= 10000 AND event_kind < 20000);
```
**`event_tags` table** — denormalized single-letter tags, populated by a PostgreSQL trigger on `events`:
```sql
CREATE TABLE event_tags (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
event_id BYTEA NOT NULL,
tag_name TEXT NOT NULL, -- single-letter only (e.g. 'e', 'p', 't')
tag_value TEXT NOT NULL
);
CREATE INDEX ON event_tags (event_id);
CREATE INDEX ON event_tags (tag_name, tag_value);
-- Trigger: on INSERT/UPDATE/DELETE to events, repopulate event_tags
CREATE OR REPLACE FUNCTION process_event_tags() RETURNS TRIGGER AS $$
DECLARE
tag_element jsonb;
BEGIN
DELETE FROM event_tags WHERE event_id = OLD.event_id;
IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
FOR tag_element IN SELECT jsonb_array_elements(NEW.event_tags) LOOP
IF length(trim('"' FROM (tag_element->0)::text)) = 1
AND (tag_element->1)::text IS NOT NULL THEN
INSERT INTO event_tags (event_id, tag_name, tag_value)
VALUES (NEW.event_id,
trim('"' FROM (tag_element->0)::text),
trim('"' FROM (tag_element->1)::text));
END IF;
END LOOP;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER insert_event_tags
AFTER INSERT OR UPDATE OR DELETE ON events
FOR EACH ROW EXECUTE FUNCTION process_event_tags();
```
Tag queries (`#e`, `#p`, etc.) join `event_tags` rather than traversing the JSONB array.
**`users` and `invoices` tables** — for paid relay admission (Lightning payments):
```sql
CREATE TABLE users (
pubkey BYTEA PRIMARY KEY,
is_admitted BOOLEAN DEFAULT FALSE,
balance BIGINT DEFAULT 0, -- in msats
tos_accepted_at DATETIME
);
CREATE TABLE invoices (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
pubkey BYTEA NOT NULL,
bolt11 TEXT NOT NULL,
amount_requested BIGINT UNSIGNED NOT NULL,
amount_paid BIGINT UNSIGNED,
unit ENUM('msats','sats','btc'),
status ENUM('pending','completed','expired'),
description TEXT,
confirmed_at DATETIME,
expires_at DATETIME
);
```
### How Redis Is Used
Redis is **not** used for subscription fan-out. Subscription delivery is handled entirely in-process: each `WebSocketAdapter` holds a `Map<subscriptionId, filters[]>`, and a broadcast event walks all connected clients and filters locally.
Redis has two actual roles:
1. **Sliding-window rate limiting** — The `SlidingWindowRateLimiter` uses Redis sorted sets to track request timestamps per key (pubkey, IP, etc.) within a rolling time window:
- `ZREMRANGEBYSCORE key 0 (now - period)` — evict old entries
- `ZADD key timestamp member` — record this hit
- `ZRANGE key 0 -1` — count hits in window
- `EXPIRE key period` — auto-cleanup
- Keys follow the pattern `<pubkey>:events:<period>` or `<ip>:message:<period>`
2. **General-purpose cache** — A `RedisAdapter` wraps the client with `get`/`set`/`hasKey` helpers, used for caching admission checks (e.g. `<pubkey>:is-admitted`). The event handler has a `TODO` comment noting that the `userRepository.findByPubkey()` call should be replaced with a cache lookup — meaning this is partially implemented.
---
## Schema Definitions
### Relay-rs (Postgres)
```sql
-- Events table
CREATE TABLE "event" (
id bytea NOT NULL,
pub_key bytea NOT NULL,
created_at timestamp with time zone NOT NULL,
kind integer NOT NULL,
"content" bytea NOT NULL,
hidden bit(1) NOT NULL DEFAULT 0::bit(1),
delegated_by bytea NULL,
first_seen timestamp with time zone NOT NULL DEFAULT now(),
expires_at timestamp(0) with time zone,
CONSTRAINT event_pkey PRIMARY KEY (id)
);
CREATE INDEX event_created_at_idx ON "event" (created_at,kind);
CREATE INDEX event_pub_key_idx ON "event" (pub_key);
CREATE INDEX event_delegated_by_idx ON "event" (delegated_by);
CREATE INDEX event_expires_at_idx ON "event" (expires_at);
-- Tags table
CREATE TABLE "tag" (
id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
event_id bytea NOT NULL,
"name" varchar NOT NULL,
value bytea NULL,
value_hex bytea NULL,
CONSTRAINT tag_fk FOREIGN KEY (event_id) REFERENCES "event"(id) ON DELETE CASCADE,
CONSTRAINT unique_constraint_name UNIQUE (event_id, "name", value, value_hex)
);
CREATE INDEX tag_event_id_idx ON tag USING btree (event_id, name);
CREATE INDEX tag_value_idx ON tag USING btree (value);
CREATE INDEX tag_value_hex_idx ON tag USING btree (value_hex);
-- Account table
CREATE TABLE "account" (
pubkey varchar NOT NULL,
is_admitted BOOLEAN NOT NULL DEFAULT FALSE,
balance BIGINT NOT NULL DEFAULT 0,
tos_accepted_at TIMESTAMP,
CONSTRAINT account_pkey PRIMARY KEY (pubkey)
);
-- Invoice table
CREATE TYPE status AS ENUM ('Paid', 'Unpaid', 'Expired');
CREATE TABLE "invoice" (
payment_hash varchar NOT NULL,
pubkey varchar NOT NULL,
invoice varchar NOT NULL,
amount BIGINT NOT NULL,
status status NOT NULL DEFAULT 'Unpaid',
description varchar,
created_at timestamp,
confirmed_at timestamp,
CONSTRAINT invoice_payment_hash PRIMARY KEY (payment_hash),
CONSTRAINT invoice_pubkey_fkey FOREIGN KEY (pubkey) REFERENCES account (pubkey) ON DELETE CASCADE
);
```
### Nostr-Relay-NestJS (PostgreSQL via Kysely)
```sql
-- Events table
CREATE TABLE events (
id CHAR(64) PRIMARY KEY,
pubkey CHAR(64) NOT NULL,
created_at BIGINT NOT NULL,
kind INTEGER NOT NULL,
tags JSONB NOT NULL DEFAULT '[]',
generic_tags TEXT[] NOT NULL DEFAULT '{}',
content TEXT NOT NULL DEFAULT '',
sig CHAR(128) NOT NULL,
expired_at BIGINT,
d_tag_value TEXT,
delegator CHAR(64),
create_date TIMESTAMP NOT NULL DEFAULT now(),
update_date TIMESTAMP NOT NULL DEFAULT now(),
delete_date TIMESTAMP
);
-- Indices
CREATE EXTENSION IF NOT EXISTS btree_gin;
CREATE INDEX generic_tags_kind_idx ON events USING gin (generic_tags, kind);
CREATE INDEX pubkey_kind_created_at_idx ON events (pubkey, kind, created_at);
CREATE INDEX delegator_kind_created_at_idx ON events (delegator, kind, created_at);
CREATE INDEX created_at_kind_idx ON events (created_at, kind);
-- Generic tags table (for optimized tag queries)
CREATE TABLE generic_tags (
id SERIAL PRIMARY KEY,
tag TEXT NOT NULL,
author CHAR(64) NOT NULL,
kind INTEGER NOT NULL,
event_id CHAR(64) NOT NULL REFERENCES events(id),
created_at BIGINT NOT NULL
);
-- Indices for tag filtering
CREATE UNIQUE INDEX g_tag_event_id_idx ON generic_tags (tag, event_id);
CREATE INDEX g_tag_kind_created_at_idx ON generic_tags (tag, kind, created_at);
CREATE INDEX g_tag_created_at_idx ON generic_tags (tag, created_at);
```
Nostr-Relay-NestJS features a hybrid storage model where tags are stored both in a JSONB field (for general lookup) and a normalized `generic_tags` table (for optimized tag query performance). The schema uses Kysely for type-safe interaction.
+204
View File
@@ -0,0 +1,204 @@
# Remaining Implementation Plan — Zero SQLite on Main Thread + PG Fork Readiness
## Overview
This plan covers all remaining work to achieve two goals:
1. **Zero direct SQLite calls on lws-main** during the event loop
2. **Clean `db_ops` abstraction boundary** so the PG fork has no raw `sqlite3_*` calls outside `db_ops.c`
## Current State
### Completed (Fixes 1-5, 7 + Phases 1-3, 5)
- Global config cache with 5s TTL
- Dedup check moved to event worker
- Async COUNT queries via thread pool
- WoT auth rules cached per-session
- Special-kind EVENT store through async worker
- Subscription logging via fire-and-forget write queue
- IP ban periodic save via write queue
- g_db nulled before event loop
### Remaining
- Fix 6: Reduce reader threads (trivial)
- Fix 8: Move periodic tasks off main thread (partial — IP ban save done, monitoring/status not)
- Fix 9: Cache NIP-11 response
- Sync `store_event()` calls from admin/DM/NIP-09 paths
- Raw `sqlite3_*` calls in `thread_pool.c` and `websockets.c`
- COUNT query path in `websockets.c` still uses `json_each()` instead of `event_tags`
---
## Phase A: Move Remaining Periodic Tasks Off Main Thread
**Goal:** Eliminate the last regular SQLite calls from the lws-main event loop timer.
### A1: Move `generate_and_post_status_event()` to write worker
Currently called synchronously from the main event loop at `websockets.c:3455`.
**Changes:**
- Add `THREAD_POOL_JOB_STATUS_POST` to `thread_pool.h` job type enum
- Add executor in `thread_pool.c` that calls `generate_and_post_status_event()`
- In `websockets.c` main loop, replace direct call with `thread_pool_submit_write()` job
- The write worker has a DB connection, so `generate_stats_json()``store_event()``broadcast_event_to_subscriptions()` all work naturally
- Note: `broadcast_event_to_subscriptions()` is thread-safe (uses its own mutex)
**Files:** `thread_pool.h`, `thread_pool.c`, `websockets.c`
### A2: Move monitoring queries to read/write worker
`monitoring_on_event_stored()` and `monitoring_on_subscription_change()` in `api.c` call `generate_event_driven_monitoring()` / `generate_subscription_driven_monitoring()` which do DB queries and then broadcast ephemeral events.
**Changes:**
- Add `THREAD_POOL_JOB_MONITORING` job type
- Submit monitoring generation as a read job (it mostly does SELECTs)
- The completion broadcasts the ephemeral event via `lws_cancel_service()` + completion queue
- Alternative simpler approach: since these are throttled (default 5s) and only fire when kind 24567 subscribers exist, they could run on the write worker as fire-and-forget
**Files:** `thread_pool.h`, `thread_pool.c`, `api.c`, `websockets.c`
### A3: Move `ip_ban_cleanup()` and `ip_ban_log_stats()` to write worker
Currently called from the 60-second periodic timer at `websockets.c:3479-3480`.
**Changes:**
- Add `THREAD_POOL_JOB_IP_BAN_CLEANUP` job type (or reuse a generic callback job)
- Submit as fire-and-forget write job from the periodic timer
- `ip_ban_cleanup()` does in-memory cleanup + optional DB writes
- `ip_ban_log_stats()` is purely logging, no DB
**Files:** `thread_pool.h`, `thread_pool.c`, `ip_ban.c`, `websockets.c`
---
## Phase B: Route Remaining Sync `store_event()` Calls Through Async Worker
**Goal:** Eliminate all synchronous `store_event()` calls from lws-main context.
### Current sync `store_event()` call sites on main thread
| Call site | File | Context | Frequency |
|-----------|------|---------|-----------|
| Kind 1059 gift wrap store | `websockets.c:1795,1803,1819` | NIP-17 DM processing | Low |
| Kind 14 DM store | `websockets.c:1843` | NIP-17 DM processing | Low |
| Regular event fallback | `websockets.c:1862,1876` | Non-async-eligible events | Low |
| Kind 1059 (auth path) | `websockets.c:2594,2602,2618` | NIP-42 auth + DM | Low |
| Kind 14 (auth path) | `websockets.c:2642` | NIP-42 auth + DM | Low |
| Regular (auth path) | `websockets.c:2661,2675` | NIP-42 auth fallback | Low |
| Admin response store | `config.c:2585` | Admin API response | Very low |
| DM admin gift wrap | `dm_admin.c:389` | Admin DM response | Very low |
| NIP-09 deletion store | `nip009.c:135` | Deletion events | Low |
| Status event store | `api.c:595` | Periodic status post | Very low (handled by Phase A1) |
| Admin response event | `api.c:908` | Admin API | Very low |
| DM chat response | `api.c:1377` | Admin DM chat | Very low |
| Relay-generated event | `api.c:2350` | Relay metadata | Very low |
| DM stats response | `websockets.c:3648` | DM stats command | Very low |
### Strategy
Rather than converting each call site individually, create a **synchronous-to-async bridge**:
**Changes:**
- Create `store_event_async(cJSON* event, store_event_callback_t cb, void* ctx)` that submits the event to the write worker
- For call sites that need the result (e.g., to broadcast after store), use a completion callback
- For fire-and-forget sites (admin responses, DM responses), use NULL callback
- The existing `store_event()` wrapper becomes: submit to write worker + block on completion (for backward compat during transition)
- Eventually, all callers migrate to the async version
**Alternative simpler approach:** Since these are all low-frequency paths, and the write worker already has a DB connection, we can submit them as generic write jobs with a callback that does the store + broadcast. The main thread just needs to hand off the event JSON.
**Files:** `main.c`, `websockets.c`, `api.c`, `config.c`, `dm_admin.c`, `nip009.c`, `thread_pool.h`, `thread_pool.c`
---
## Phase C: `db_ops` Abstraction Cleanup for PG Fork
**Goal:** Ensure all raw `sqlite3_*` calls are inside `db_ops.c` only. External code uses only `db_ops.h` functions.
### C1: Add `db_open_worker_connection()` / `db_close_worker_connection()` to `db_ops.h`
Currently, `thread_pool.c:208` and `websockets.c:537` call `sqlite3_open_v2()` directly.
**Changes:**
- Add to `db_ops.h`:
```c
void* db_open_worker_connection(void); // Returns opaque connection handle
void db_close_worker_connection(void* conn);
```
- Implementation in `db_ops.c`: opens a new SQLite connection with WAL + busy timeout
- `thread_pool.c` worker init calls `db_open_worker_connection()` instead of raw `sqlite3_open_v2()`
- `websockets.c` event-worker calls `db_open_worker_connection()` instead of raw `sqlite3_open_v2()`
- Remove `#include <sqlite3.h>` from `thread_pool.c` and `websockets.c`
**Files:** `db_ops.h`, `db_ops.c`, `thread_pool.c`, `websockets.c`
### C2: Audit and remove remaining `sqlite3` includes outside `db_ops.c`
After C1, grep for any remaining `sqlite3` references outside `db_ops.c` and eliminate them.
**Files:** All `.c` files
### C3: Unify COUNT query tag path
The COUNT query builder in `websockets.c:3849` still uses `json_each(json(tags))` while the REQ query builder in `main.c:1604` uses the indexed `event_tags` table. Unify to use `event_tags` for consistency.
**Files:** `websockets.c`
---
## Phase D: Quick Wins
### D1: Fix 6 — Reduce reader threads from 4 to 1
Change default in `thread_pool_init()` or make configurable. With all reads going through the pool, a single reader is sufficient unless profiling shows otherwise.
**Files:** `thread_pool.c` or config default
### D2: Fix 9 — Cache NIP-11 response
Cache the serialized NIP-11 JSON string with a 60-second TTL. Invalidate on admin config change.
**Files:** `nip011.c`
---
## Implementation Order
| # | Task | Risk | Dependencies |
|---|------|------|-------------|
| 1 | A3: ip_ban_cleanup to write worker | Very Low | None |
| 2 | A1: status post to write worker | Low | None |
| 3 | A2: monitoring to worker | Low | None |
| 4 | D1: reduce reader threads to 1 | Very Low | None |
| 5 | D2: cache NIP-11 response | Very Low | None |
| 6 | C1: db_open/close_worker_connection | Low | None |
| 7 | C3: unify COUNT tag query path | Low | None |
| 8 | C2: audit/remove sqlite3 includes | Very Low | C1 |
| 9 | B: route sync store_event through async | Medium | A1 (status post already handled) |
## Expected Outcome
After all phases:
- **lws-main has zero SQLite calls** during the event loop
- **All `sqlite3_*` calls are inside `db_ops.c`** — clean abstraction boundary
- **PG fork can replace `db_ops.c`** without touching any other file
- Thread model is fully realized:
- `lws-main` = WebSocket I/O + JSON parse + subscription match + completion drain
- `event-worker` = signature verify + dedup + store submission
- `db-read` = REQ queries + COUNT queries + monitoring reads
- `db-write` = event INSERTs + subscription logging + periodic tasks + admin writes
```mermaid
flowchart LR
CLIENT[Nostr Clients] --> LWS[lws-main<br/>Zero SQLite<br/>WS I/O + JSON + Sub Match]
LWS -->|EVENT| EW[event-worker<br/>Verify + Dedup]
EW -->|store job| DW[db-write<br/>INSERT events<br/>Sub logging<br/>Periodic tasks<br/>Admin writes]
EW -->|completion| LWS
DW -->|completion| LWS
LWS -->|REQ/COUNT| DR[db-read<br/>SELECT queries<br/>Monitoring reads]
DR -->|completion| LWS
LWS -->|OK/EVENT/EOSE/COUNT| CLIENT
```
+379
View File
@@ -0,0 +1,379 @@
# Plan: Remove All SQLite from lws-main Thread
## Problem Statement
After implementing the 4 offload fixes (v2.1.4), `lws-main` still consumes **53.87% avg CPU** (down from 68.71%). Perf profiling shows **100% of the top symbols are SQLite** — B-tree traversal, page cache, VFS reads. The worker threads (`db-read`, `db-write`, `event-worker`) are nearly idle at 0.00.1%.
The goal is to eliminate all synchronous SQLite access from `lws-main` so it only does: JSON parsing, WebSocket I/O, subscription matching (in-memory), and message queuing.
## Thread Architecture Change
Reduce from 7 threads to 4:
```
Current: lws-main, db-read-1, db-read-2, db-read-3, db-read-4, db-write, event-worker
Proposed: lws-main, db-read, db-write, event-worker
```
The 4 reader threads are overkill — they average 0.070.10% CPU each. A single `db-read` thread is sufficient for the current workload. This can be made configurable later if needed.
## Remaining SQLite Calls on lws-main
Every synchronous SQLite call that currently runs on `lws-main`:
### Hot Path — Per-Event (every incoming EVENT message)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `event_id_exists_in_db()` | websockets.c:1324, 2134 | Every EVENT | ~10-50us per B-tree lookup |
| `get_config_value("relay_pubkey")` | websockets.c:2050 | Every kind-14 EVENT | SQLite SELECT |
| `get_config_value("admin_pubkey")` | websockets.c:2073 | Every kind-23456 EVENT | SQLite SELECT |
### Hot Path — Per-Connection (every new WebSocket connection)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_bool("trust_proxy_headers")` | websockets.c:1071 | Every connection | SQLite SELECT |
| `get_config_bool("nip42_auth_required_events")` | websockets.c:1134 | Every connection | SQLite SELECT |
| `get_config_bool("nip42_auth_required_subscriptions")` | websockets.c:1135 | Every connection | SQLite SELECT |
### Hot Path — Per-REQ (every subscription request)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_bool("expiration_enabled")` | main.c:1424 | Every REQ | SQLite SELECT |
| `get_config_bool("expiration_filter")` | main.c:1425 | Every REQ | SQLite SELECT |
| `check_database_auth_rules()` | websockets.c:1706 | Every REQ when WoT enabled | SQLite SELECT |
### Medium Path — Per-COUNT (NIP-45)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `thread_pool_execute_count_sync()` | websockets.c:3689 | Every COUNT message | Blocks main thread waiting for db-read |
### Low Path — Periodic (every 60s or on timer)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `refresh_hot_config_if_needed()` | websockets.c:228-247 | Every 5s | 8 SQLite SELECTs |
| `generate_and_post_status_event()` | websockets.c:3213 | Every N hours | store_event + broadcast |
| `ip_ban_cleanup/log_stats` | websockets.c:3004-3005 | Every 60s | SQLite reads/writes |
### Per-EVENT — Validator Path (called from event-worker AND sync path)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_value("admin_pubkey")` | request_validator.c:308 | Every kind-23456 EVENT | SQLite SELECT |
| `get_config_value("nip42_auth_enabled")` | request_validator.c:326 | Every EVENT | SQLite SELECT |
| `get_config_bool("pow_enabled")` | request_validator.c:370 | Every EVENT | SQLite SELECT |
| `get_config_int("pow_min_difficulty")` | request_validator.c:371 | Every EVENT | SQLite SELECT |
| `get_config_int("pow_validation_flags")` | request_validator.c:372 | Every EVENT | SQLite SELECT |
| `get_config_int("nip40_expiration_grace_period")` | request_validator.c:432 | Every EVENT with expiration | SQLite SELECT |
| `get_config_value("auth_enabled")` | request_validator.c:212 | Every EVENT (reload_auth_config) | SQLite SELECT |
| `get_config_value("auth_rules_enabled")` | request_validator.c:220 | Every EVENT (reload_auth_config) | SQLite SELECT |
| `db_count_active_whitelist_rules()` | request_validator.c:536 | Every 5s (cached) | Opens separate SQLite connection |
**Note:** These run on the **event-worker** thread for async-eligible events, but on **lws-main** for special kinds (14, 1059, 23456) that go through the sync validation path. They also run on lws-main for the `nostr_validate_unified_request()` call in the sync fallback.
### Per-EVENT — NIP-13 PoW Validation
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_bool("pow_enabled")` | nip013.c:27 | Every EVENT | SQLite SELECT |
| `get_config_int("pow_min_difficulty")` | nip013.c:28 | Every EVENT | SQLite SELECT |
| `get_config_value("pow_mode")` | nip013.c:29 | Every EVENT | SQLite SELECT |
### Per-Broadcast — NIP-40 Expiration Check
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_bool("expiration_enabled")` | subscriptions.c:764 | Every broadcast | SQLite SELECT |
| `get_config_bool("expiration_filter")` | subscriptions.c:765 | Every broadcast | SQLite SELECT |
### Per-NIP-11 Request (HTTP, not WebSocket)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| 15+ `get_config_value()` calls | nip011.c:96-110 | Every NIP-11 HTTP request | 15 SQLite SELECTs |
| 10+ `get_config_int()`/`get_config_bool()` calls | nip011.c:113-123 | Every NIP-11 HTTP request | 10 SQLite SELECTs |
### Per-NIP-42 Auth Event
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_value("relay_url")` | nip042.c:99 | Every AUTH event | SQLite SELECT |
| `get_config_int("relay_port")` | nip042.c:103 | Every AUTH event (fallback) | SQLite SELECT |
### Per-NIP-40 Expiration Check (EVENT submission)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| 5 `get_config_bool()`/`get_config_int()` calls | nip040.c:37-41 | Every EVENT with expiration tag | 5 SQLite SELECTs |
### Subscription Lifecycle (per sub create/close/disconnect)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `db_log_subscription_created()` | subscriptions.c:1043 | Every REQ | SQLite INSERT |
| `db_log_subscription_closed()` | subscriptions.c:1053 | Every CLOSE | SQLite INSERT + UPDATE |
| `db_log_subscription_disconnected()` | subscriptions.c:1060 | Every disconnect | SQLite UPDATE + INSERT |
| `db_update_subscription_events_sent()` | subscriptions.c:1088 | Every broadcast match | SQLite UPDATE |
| `db_cleanup_orphaned_subscriptions()` | subscriptions.c:1100 | Startup only | SQLite DELETE |
### IP Ban System (per-connection and periodic)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_value("idle_ban_whitelist")` | ip_ban.c:254 | Every ban check | SQLite SELECT |
| `get_config_bool("auth_fail_ban_enabled")` | ip_ban.c:301, 343 | Every ban check + failure | SQLite SELECT |
| `get_config_bool("idle_ban_enabled")` | ip_ban.c:307, 394 | Every ban check + idle failure | SQLite SELECT |
| 6+ `get_config_int()` calls | ip_ban.c:345-399, 464-465 | Every failure recording | SQLite SELECTs |
| `db_prepare()`/`db_step_stmt()` for ip_bans | ip_ban.c:141-238 | Load at startup + periodic save | SQLite reads/writes |
### Special Kind Sync Path (kinds 14, 1059, 23456)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `store_event()` | websockets.c:1548-1629 | Every special-kind EVENT | Full sync store |
| `process_admin_event_in_config()` | websockets.c:1468 | Every kind-23456 | Config DB writes |
| `process_nip17_admin_message()` | websockets.c:1523 | Every kind-1059 when enabled | Decrypt + DB |
| `process_dm_stats_command()` | websockets.c:3240 | Admin DM stats | Decrypt + query + encrypt |
| `generate_stats_json()` | api.c:1167 (via dm_admin.c:805) | Admin stats command | Multiple SQLite queries |
### DM Admin Path (called from lws-main for kind 1059/14)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `get_config_value("relay_pubkey")` | dm_admin.c:358, 451 | Every admin DM | SQLite SELECT |
| `get_config_value("admin_pubkey")` | dm_admin.c:504 | Every admin DM | SQLite SELECT |
| `get_config_int("nip59_timestamp_max_delay_sec")` | dm_admin.c:373 | Every admin DM | SQLite SELECT |
| `get_config_int("wot_enabled")` | dm_admin.c:613, 640, 660 | WoT commands | SQLite SELECT |
### API/Monitoring (called from lws-main periodic timer)
| Call | Location | Frequency | Cost |
|------|----------|-----------|------|
| `generate_stats_json()` | api.c:1167 | Status post + admin stats | ~10 SQLite queries |
| `query_time_based_statistics()` | api.c:102 | Monitoring events | SQLite queries |
| `query_subscription_details()` | api.c:197 | Monitoring events | SQLite queries |
| `get_config_value("relay_pubkey")` | api.c:419, 821, 1291 | Every monitoring event | SQLite SELECT |
| `get_config_int("kind_1_status_posts_hours")` | api.c:547 | Status post check | SQLite SELECT |
| `get_config_int("kind_24567_reporting_throttle_sec")` | api.c:60 | Monitoring throttle | SQLite SELECT |
## Implementation Plan
### Fix 1: Create Global Config Cache — Eliminate ALL get_config_* SQLite Calls
**Impact: Very High** — eliminates 50+ distinct `get_config_value/int/bool` call sites across 8 source files
The audit found `get_config_value/int/bool` calls in: `websockets.c`, `main.c`, `subscriptions.c`, `request_validator.c`, `nip011.c`, `nip013.c`, `nip040.c`, `nip042.c`, `ip_ban.c`, `dm_admin.c`, `api.c`, `config.c`. Every single one does a `SELECT value FROM config WHERE key=?` SQLite query.
**Approach:** Replace `get_config_value_from_table()` with an in-memory hash map that is loaded once at startup and refreshed periodically (every 5s) or on admin config change events.
**Changes:**
1. **New: `config_cache` in config.c** — A simple hash map or fixed array of key-value pairs loaded from the config table:
```c
typedef struct {
char key[64];
char value[512];
} config_cache_entry_t;
static config_cache_entry_t g_config_cache[256];
static int g_config_cache_count = 0;
static time_t g_config_cache_last_refresh = 0;
static pthread_mutex_t g_config_cache_mutex;
#define CONFIG_CACHE_TTL_SEC 5
```
2. **Modify `get_config_value_from_table()`** — Instead of querying SQLite, look up in the in-memory cache. If cache is stale, refresh from SQLite (but only once per TTL period, not per call).
3. **Add `config_cache_refresh()`** — Loads all rows from `SELECT key, value FROM config` into the hash map. Called:
- Once at startup after config table is populated
- Every 5 seconds from the existing `refresh_hot_config_if_needed()` timer
- Immediately after `process_admin_event_in_config()` modifies config
4. **Thread safety:** The cache is read from lws-main and event-worker threads. Use a read-write lock or double-buffer pattern. Since config changes are rare, a simple mutex with short hold time is fine.
5. **Remove the existing `hot_config_cache_t`** in websockets.c — it becomes redundant since the global config cache serves the same purpose with broader coverage.
This single change eliminates SQLite calls from:
- Per-connection: `trust_proxy_headers`, `nip42_auth_required_*` (websockets.c)
- Per-REQ: `expiration_enabled`, `expiration_filter` (main.c)
- Per-EVENT: `admin_pubkey`, `relay_pubkey`, `nip42_auth_enabled`, `pow_*` (request_validator.c, nip013.c)
- Per-broadcast: `expiration_enabled`, `expiration_filter` (subscriptions.c)
- Per-NIP-11: 25+ config values (nip011.c)
- Per-NIP-42: `relay_url`, `relay_port` (nip042.c)
- Per-NIP-40: 5 expiration config values (nip040.c)
- Per-IP-ban: `idle_ban_*`, `auth_fail_*` config values (ip_ban.c)
- Per-admin-DM: `relay_pubkey`, `admin_pubkey`, `wot_enabled` (dm_admin.c)
- Per-monitoring: `relay_pubkey`, throttle values (api.c)
### Fix 2: Move Duplicate Check to Event Worker Thread
**Impact: High** — eliminates the most frequent per-EVENT SQLite call from lws-main
Currently `event_id_exists_in_db()` runs on lws-main before async submission. Move it into the `async_event_worker_main()` function, which already has its own SQLite connection.
**Changes:**
- Remove `event_id_exists_in_db()` calls from both EVENT paths in websockets.c (lines 1324 and 2134)
- Add the duplicate check at the start of `async_event_worker_main()` before `nostr_validate_unified_request()`
- If duplicate found, set `completion->success = 1` and `completion->error_message = "duplicate: already have this event"` and skip crypto
- The completion handler already sends OK responses, so this works seamlessly
**Trade-off:** Without the early dedup check on lws-main, duplicate events will be submitted to the event worker queue and consume a queue slot + thread wakeup before being detected. This is acceptable because:
- The worker thread dedup check is still fast (B-tree index lookup)
- It avoids the much larger cost of running SQLite on lws-main for every event
- Queue depth is 4096, so even under heavy duplicate traffic this won't overflow
### Fix 3: Make COUNT Queries Async
**Impact: Medium** — eliminates blocking wait on lws-main for NIP-45 COUNT
Currently `handle_count_message()` calls `thread_pool_execute_count_sync()` which blocks lws-main waiting for the db-read worker. Convert to the same async pattern used for REQ queries.
**Changes:**
- Create `count_async_state_t` similar to `req_async_state_t`
- Create `count_async_completion_t` with count result
- Add `submit_count_query_async()` that uses `thread_pool_submit_read()` with a callback
- The callback pushes to a count completion queue
- Add `process_count_async_completions()` to drain the queue on lws-main and send COUNT responses
- Call it from the main event loop alongside `process_req_async_completions()`
### Fix 4: Cache Auth Rules Check Result
**Impact: Low-Medium** — eliminates per-REQ SQLite when WoT is enabled
`check_database_auth_rules()` at websockets.c:1706 runs a SQLite query for every REQ when `wot_enabled == 2`. Cache the result per-session.
**Changes:**
- Add `wot_checked` and `wot_allowed` fields to `per_session_data`
- On first REQ, call `check_database_auth_rules()` and cache the result
- On subsequent REQs from the same session, use the cached value
- Reset cache when auth state changes (e.g., after NIP-42 AUTH)
### Fix 5: Move Special-Kind EVENT Store to Event Worker
**Impact: Low** — special kinds are rare but currently do full sync store on lws-main
Kinds 14, 1059, and 23456 are currently excluded from async processing because they have special handling. However, the `store_event()` call within those paths still blocks lws-main.
**Changes:**
- For kind 1059 and kind 14: after special processing completes, submit the store to the event worker instead of calling `store_event()` synchronously
- For kind 23456: admin events are not stored (processed by admin API), so no change needed
- This requires the special processing (decryption, admin check) to still happen on lws-main (it needs `pss` context), but the final DB write goes async
### Fix 6: Reduce Reader Threads from 4 to 1
**Impact: Resource savings** — 3 fewer threads, 3 fewer SQLite connections
**Changes:**
- Change default `reader_threads` in `thread_pool_init()` from 4 to 1
- Or make it configurable via config with default 1
- The single `db-read` thread handles all REQ and COUNT queries sequentially
- If future profiling shows the read thread becoming a bottleneck, increase back to 2+
### Fix 7: Move Subscription Logging Off Main Thread
**Impact: Medium** — eliminates per-REQ INSERT, per-CLOSE UPDATE, per-broadcast UPDATE
The subscription lifecycle functions in subscriptions.c do synchronous SQLite writes on lws-main:
- `db_log_subscription_created()` — INSERT on every REQ
- `db_log_subscription_closed()` — INSERT + UPDATE on every CLOSE
- `db_log_subscription_disconnected()` — UPDATE + INSERT on every disconnect
- `db_update_subscription_events_sent()` — UPDATE on every broadcast match
**Changes:**
- Submit these as fire-and-forget write jobs to the db-write thread via `thread_pool_submit_write()`
- Create a new job type `THREAD_POOL_JOB_SUBSCRIPTION_LOG` or simply use a generic callback job
- The main thread doesn't need to wait for these to complete — they're purely observational logging
### Fix 8: Move Periodic Tasks Off Main Thread
**Impact: Low** — these run every 60s but can still cause latency spikes
- `generate_and_post_status_event()`: Submit as an async job to the event worker
- `ip_ban_cleanup()`/`ip_ban_log_stats()`/`ip_ban_save()`: These do SQLite reads/writes. Move to a periodic job on the db-write thread
- Config cache refresh (Fix 1) replaces the old `refresh_hot_config_if_needed()` SQLite queries
### Fix 9: Cache NIP-11 Response
**Impact: Low-Medium** — eliminates 25+ SQLite SELECTs per NIP-11 HTTP request
`generate_relay_info_json()` in nip011.c calls `get_config_value()` 15 times and `get_config_int()`/`get_config_bool()` 10 times. After Fix 1 (global config cache), these become in-memory lookups. But we can go further:
**Changes:**
- Cache the serialized NIP-11 JSON response string with a TTL (e.g., 60 seconds)
- On NIP-11 request, return the cached string directly without rebuilding
- Invalidate on admin config change events
## Implementation Order
| # | Fix | Risk | Complexity |
|---|-----|------|------------|
| 1 | Global config cache (replaces all get_config_* SQLite) | Low | Medium |
| 2 | Move dedup check to event worker | Low | Low |
| 3 | Async COUNT queries | Low | Medium |
| 4 | Cache WoT auth rules per-session | Very Low | Low |
| 5 | Async store for special kinds | Medium | Medium |
| 6 | Reduce reader threads to 1 | Very Low | Trivial |
| 7 | Move subscription logging off main thread | Low | Low-Medium |
| 8 | Move periodic tasks off main thread | Low | Medium |
| 9 | Cache NIP-11 response | Very Low | Low |
## Expected Impact
After all 9 fixes, `lws-main` should have **zero direct SQLite calls**. Its work becomes:
- JSON parse incoming messages (~fast)
- In-memory subscription matching (~fast)
- In-memory config lookups (~fast, hash map)
- Message formatting and queuing (~fast)
- `lws_service()` I/O (~fast)
- Draining completion queues (~fast)
### Impact by fix
| Fix | SQLite calls eliminated | Frequency |
|-----|------------------------|-----------|
| Fix 1: Global config cache | ~50+ `get_config_*` call sites across 8 files | Per-event, per-connection, per-REQ, per-broadcast, per-NIP-11 |
| Fix 2: Dedup to event worker | 2 `event_id_exists_in_db()` calls | Per-EVENT |
| Fix 3: Async COUNT | 1 `thread_pool_execute_count_sync()` | Per-COUNT message |
| Fix 4: WoT cache per-session | 1 `check_database_auth_rules()` | Per-REQ when WoT enabled |
| Fix 5: Async special-kind store | ~6 `store_event()` calls | Per special-kind EVENT |
| Fix 6: Reduce readers | N/A (resource savings) | N/A |
| Fix 7: Async subscription logging | 4 `db_log_*`/`db_update_*` calls | Per-REQ, per-CLOSE, per-broadcast |
| Fix 8: Async periodic tasks | `generate_stats_json()`, `ip_ban_*` | Every 60s |
| Fix 9: Cache NIP-11 | 25+ config lookups per request | Per NIP-11 HTTP request |
Estimated main-thread CPU reduction: from **54% to ~5-10%** (mostly WebSocket I/O, subscription matching, and JSON parsing).
## Thread Model After Changes
```
lws-main — WebSocket I/O, JSON parse, subscription match, message queue
NO SQLite. Pure compute + I/O.
db-read — All SELECT queries: REQ results, COUNT results, config refresh,
auth rule checks, event dedup checks via completion queue
db-write — All INSERT/UPDATE: event storage, tag inserts, IP ban persistence,
status event generation
event-worker — EVENT validation: signature verification, then submits store to
db-write via completion queue, results flow back to lws-main
```
```mermaid
flowchart LR
CLIENT[Nostr Clients] --> LWS[lws-main<br/>WebSocket I/O<br/>JSON parse<br/>Sub matching<br/>NO SQLite]
LWS -->|EVENT submit| EW[event-worker<br/>Sig verify<br/>Dedup check]
EW -->|store job| DW[db-write<br/>INSERT events<br/>INSERT tags<br/>IP ban save]
EW -->|completion| LWS
DW -->|completion| LWS
LWS -->|REQ/COUNT submit| DR[db-read<br/>SELECT queries<br/>Config refresh<br/>Auth checks]
DR -->|completion| LWS
LWS -->|OK/EVENT/EOSE| CLIENT
```
-91
View File
@@ -1,91 +0,0 @@
# PostgreSQL Nostr Relay Architecture Analysis
This document summarizes common approaches and best practices for building Nostr relays backed by PostgreSQL, based on industry-standard designs and existing open-source relay implementations.
## Identified Projects
* **Nostrss**: High-performance Rust-based relay.
* **Nostrgres**: Focused on PostgreSQL integration with complex event filtering.
* **Relay-rs (Postgres branch)**: General purpose high-throughput relay.
## Detailed Analysis
### 1. Common Schema Patterns
* **Event Storage (JSONB)**: Most PostgreSQL-backed relays store the raw event as a JSONB object in a central `events` table. This provides flexibility for the evolving Nostr spec while allowing efficient extraction of fields.
* **Tag Denormalization**: While the raw event is in JSONB, performant relays extract tags (like `p`, `e`, `t`, `d`) into a separate `tags` table with foreign key relationships to the `events` table. This avoids slow JSONB traversal during complex filter queries.
* **Author/Publisher Tracking**: A dedicated `authors` or `pubkeys` table is typically used to maintain indexing on the `pubkey` field, enabling quick lookups of user activity.
### 2. Performance Optimization
* **GIN Indexes on JSONB**: Crucial for filtering on specific event tags or custom properties stored within the event object. GIN indexes with `jsonb_path_ops` are generally preferred for equality checks.
* **Time-Series Partitioning**: Given the append-only nature of Nostr, partitioning the `events` table by time (e.g., daily or weekly chunks) is highly recommended. This significantly improves query performance for recent data and simplifies data expiration/deletion.
* **Clustering**: Clustering the `events` table by the timestamp index can reduce disk I/O, as it keeps temporally related events physically adjacent on the disk.
### 3. Schema Management Approaches
* **Migrations**: Most mature relays utilize migration tools (like `Flyway`, `Diesel migrations`, or `Golang-migrate`) to version control database schema changes. This is critical for production stability.
* **Auto-generation**: Some lightweight prototypes auto-generate schemas at startup. This is generally discouraged for production due to the risk of destructive changes, data loss, or blocking DDL operations on large tables.
## Recommendations for Building a New Relay
1. **Prioritize Denormalization**: Do not rely solely on JSONB queries for high-traffic filters. Extract indexed tags into dedicated columns or tables.
2. **Use Partitioning from Day One**: Implementing native PostgreSQL partitioning (e.g., using `pg_partman`) is much easier before the dataset grows to millions of rows.
3. **Connection Pooling**: PostgreSQL requires aggressive connection management. Use a high-performance pooler like `PgBouncer` to handle the large number of concurrent, short-lived connections common in WebSocket-based relay traffic.
4. **Asynchronous Writes**: Decouple the WebSocket ingestion thread from the database writer thread to ensure that slow database writes do not impact the relay's responsiveness.
## Schema Definitions
### Relay-rs (Postgres)
```sql
-- Events table
CREATE TABLE "event" (
id bytea NOT NULL,
pub_key bytea NOT NULL,
created_at timestamp with time zone NOT NULL,
kind integer NOT NULL,
"content" bytea NOT NULL,
hidden bit(1) NOT NULL DEFAULT 0::bit(1),
delegated_by bytea NULL,
first_seen timestamp with time zone NOT NULL DEFAULT now(),
expires_at timestamp(0) with time zone,
CONSTRAINT event_pkey PRIMARY KEY (id)
);
CREATE INDEX event_created_at_idx ON "event" (created_at,kind);
CREATE INDEX event_pub_key_idx ON "event" (pub_key);
CREATE INDEX event_delegated_by_idx ON "event" (delegated_by);
CREATE INDEX event_expires_at_idx ON "event" (expires_at);
-- Tags table
CREATE TABLE "tag" (
id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
event_id bytea NOT NULL,
"name" varchar NOT NULL,
value bytea NULL,
value_hex bytea NULL,
CONSTRAINT tag_fk FOREIGN KEY (event_id) REFERENCES "event"(id) ON DELETE CASCADE,
CONSTRAINT unique_constraint_name UNIQUE (event_id, "name", value, value_hex)
);
CREATE INDEX tag_event_id_idx ON tag USING btree (event_id, name);
CREATE INDEX tag_value_idx ON tag USING btree (value);
CREATE INDEX tag_value_hex_idx ON tag USING btree (value_hex);
-- Account table
CREATE TABLE "account" (
pubkey varchar NOT NULL,
is_admitted BOOLEAN NOT NULL DEFAULT FALSE,
balance BIGINT NOT NULL DEFAULT 0,
tos_accepted_at TIMESTAMP,
CONSTRAINT account_pkey PRIMARY KEY (pubkey)
);
-- Invoice table
CREATE TYPE status AS ENUM ('Paid', 'Unpaid', 'Expired');
CREATE TABLE "invoice" (
payment_hash varchar NOT NULL,
pubkey varchar NOT NULL,
invoice varchar NOT NULL,
amount BIGINT NOT NULL,
status status NOT NULL DEFAULT 'Unpaid',
description varchar,
created_at timestamp,
confirmed_at timestamp,
CONSTRAINT invoice_payment_hash PRIMARY KEY (payment_hash),
CONSTRAINT invoice_pubkey_fkey FOREIGN KEY (pubkey) REFERENCES account (pubkey) ON DELETE CASCADE
);
```
+1 -1
View File
@@ -1 +1 @@
484178
763795
+55 -8
View File
@@ -19,6 +19,7 @@ int get_active_connection_count(void);
#include <unistd.h>
#include <strings.h>
#include <stdbool.h>
#include <sqlite3.h>
#include "api.h"
#include "embedded_web_content.h"
#include "config.h"
@@ -55,6 +56,40 @@ int generate_monitoring_event_for_type(const char* d_tag_value, cJSON* (*query_f
// Forward declaration for CPU metrics query function
cJSON* query_cpu_metrics(void);
static int api_open_temp_db_connection(sqlite3** out_db) {
if (!out_db) {
return -1;
}
*out_db = NULL;
const char* db_path = db_get_database_path();
if (!db_path || db_path[0] == '\0') {
return -1;
}
if (sqlite3_open_v2(db_path, out_db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK) {
if (*out_db) {
sqlite3_close(*out_db);
*out_db = NULL;
}
return -1;
}
sqlite3_exec(*out_db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
sqlite3_busy_timeout(*out_db, 5000);
db_set_thread_connection(*out_db);
return 0;
}
static void api_close_temp_db_connection(sqlite3* db) {
if (!db) {
return;
}
db_clear_thread_connection();
sqlite3_close(db);
}
// Monitoring system helper functions
int get_monitoring_throttle_seconds(void) {
return get_config_int("kind_24567_reporting_throttle_sec", 5);
@@ -62,7 +97,8 @@ int get_monitoring_throttle_seconds(void) {
// Query event kind distribution from database
cJSON* query_event_kind_distribution(void) {
if (!db_is_available()) {
sqlite3* temp_db = NULL;
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
DEBUG_ERROR("Database not available for monitoring query");
return NULL;
}
@@ -95,12 +131,14 @@ cJSON* query_event_kind_distribution(void) {
cJSON_AddNumberToObject(distribution, "total_events", total_events);
cJSON_AddItemToObject(distribution, "kinds", kinds_array);
api_close_temp_db_connection(temp_db);
return distribution;
}
// Query time-based statistics from database
cJSON* query_time_based_statistics(void) {
if (!db_is_available()) {
sqlite3* temp_db = NULL;
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
DEBUG_ERROR("Database not available for time stats query");
return NULL;
}
@@ -146,12 +184,14 @@ cJSON* query_time_based_statistics(void) {
cJSON_AddItemToObject(time_stats, "periods", periods_array);
cJSON_AddNumberToObject(time_stats, "total_events", total_events);
api_close_temp_db_connection(temp_db);
return time_stats;
}
// Query top pubkeys by event count from database
cJSON* query_top_pubkeys(void) {
if (!db_is_available()) {
sqlite3* temp_db = NULL;
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
DEBUG_ERROR("Database not available for top pubkeys query");
return NULL;
}
@@ -163,6 +203,7 @@ cJSON* query_top_pubkeys(void) {
cJSON* rows = db_get_top_pubkeys_rows(10);
if (!rows) {
cJSON_Delete(top_pubkeys);
api_close_temp_db_connection(temp_db);
DEBUG_ERROR("Failed to query top pubkeys rows");
return NULL;
}
@@ -188,6 +229,7 @@ cJSON* query_top_pubkeys(void) {
cJSON_AddItemToObject(top_pubkeys, "pubkeys", pubkeys_array);
cJSON_AddNumberToObject(top_pubkeys, "total_events", total_events);
api_close_temp_db_connection(temp_db);
return top_pubkeys;
}
@@ -195,7 +237,8 @@ cJSON* query_top_pubkeys(void) {
// Query detailed subscription information from database log (ADMIN ONLY)
// Uses subscriptions table instead of in-memory iteration to avoid mutex contention
cJSON* query_subscription_details(void) {
if (!db_is_available()) {
sqlite3* temp_db = NULL;
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
DEBUG_ERROR("Database not available for subscription details query");
return NULL;
}
@@ -226,6 +269,7 @@ cJSON* query_subscription_details(void) {
cJSON_Delete(subscriptions_data);
cJSON_Delete(data);
cJSON_Delete(subscriptions_array);
api_close_temp_db_connection(temp_db);
DEBUG_ERROR("Failed to query subscription details rows");
return NULL;
}
@@ -278,9 +322,9 @@ cJSON* query_subscription_details(void) {
// 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) {
// Resolve live session data only if this wsi is still tracked.
struct per_session_data* pss = NULL;
if (websocket_get_live_pss(wsi, &pss) && pss) {
db_queries = pss->db_queries_executed;
db_rows = pss->db_rows_returned;
@@ -341,6 +385,7 @@ cJSON* query_subscription_details(void) {
DEBUG_LOG("Total subscriptions found: %d", row_count);
DEBUG_LOG("=== END SUBSCRIPTION_DETAILS QUERY DEBUG ===");
api_close_temp_db_connection(temp_db);
return subscriptions_data;
}
@@ -1165,7 +1210,8 @@ cJSON* query_cpu_metrics(void) {
// Generate stats JSON from database queries
char* generate_stats_json(void) {
if (!db_is_available()) {
sqlite3* temp_db = NULL;
if (!db_is_available() && api_open_temp_db_connection(&temp_db) != 0) {
DEBUG_ERROR("Database not available for stats generation");
return NULL;
}
@@ -1274,6 +1320,7 @@ char* generate_stats_json(void) {
DEBUG_ERROR("Failed to generate stats JSON");
}
api_close_temp_db_connection(temp_db);
return json_string;
}
+273 -68
View File
@@ -108,6 +108,9 @@ static cJSON* g_pending_config_event = NULL;
// Temporary storage for relay private key during first-time setup
static char g_temp_relay_privkey[65] = {0};
// Runtime cache for relay private key to support strict mode (g_db = NULL)
static char g_cached_relay_privkey[65] = {0};
// ================================
// UTILITY FUNCTIONS
// ================================
@@ -251,14 +254,21 @@ const char* get_config_value(const char* key) {
if (!key) {
return NULL;
}
return get_config_value_from_table(key);
const char* cached = get_config_value_from_table(key);
if (!cached) {
return NULL;
}
// Public API returns an owned string; callers may free it.
return cached;
}
int get_config_int(const char* key, int default_value) {
if (!key) {
return default_value;
}
const char* value = get_config_value_from_table(key);
const char* value = get_config_value(key);
if (!value) {
return default_value;
}
@@ -271,7 +281,7 @@ int get_config_bool(const char* key, int default_value) {
if (!key) {
return default_value;
}
const char* value = get_config_value_from_table(key);
const char* value = get_config_value(key);
if (!value) {
return default_value;
}
@@ -405,6 +415,9 @@ void cleanup_configuration_system(void) {
g_pending_config_event = NULL;
}
// Clear cached sensitive material
memset(g_cached_relay_privkey, 0, sizeof(g_cached_relay_privkey));
// Configuration system now uses direct database queries instead of cache
// No cleanup needed for cache system
}
@@ -486,6 +499,10 @@ int store_relay_private_key(const char* relay_privkey_hex) {
}
if (db_store_relay_private_key_hex(relay_privkey_hex) == 0) {
// Keep an in-memory runtime copy for strict mode where g_db is NULL
memset(g_cached_relay_privkey, 0, sizeof(g_cached_relay_privkey));
strncpy(g_cached_relay_privkey, relay_privkey_hex, sizeof(g_cached_relay_privkey) - 1);
g_cached_relay_privkey[sizeof(g_cached_relay_privkey) - 1] = '\0';
return 0;
}
@@ -494,11 +511,16 @@ int store_relay_private_key(const char* relay_privkey_hex) {
}
char* get_relay_private_key(void) {
// Fast path for strict runtime mode where g_db may be NULL
if (strlen(g_cached_relay_privkey) == 64) {
return strdup(g_cached_relay_privkey);
}
if (!db_is_available()) {
DEBUG_ERROR("Database not available for relay private key retrieval");
return NULL;
}
char* private_key = db_get_relay_private_key_hex_dup();
if (!private_key || strlen(private_key) != 64) {
if (private_key) {
@@ -508,9 +530,35 @@ char* get_relay_private_key(void) {
return NULL;
}
// Prime runtime cache from DB for subsequent strict-mode operations
memset(g_cached_relay_privkey, 0, sizeof(g_cached_relay_privkey));
strncpy(g_cached_relay_privkey, private_key, sizeof(g_cached_relay_privkey) - 1);
g_cached_relay_privkey[sizeof(g_cached_relay_privkey) - 1] = '\0';
return private_key;
}
int preload_relay_private_key_cache(void) {
if (!db_is_available()) {
DEBUG_ERROR("Database not available for relay private key cache preload");
return -1;
}
char* private_key = db_get_relay_private_key_hex_dup();
if (!private_key || strlen(private_key) != 64) {
if (private_key) free(private_key);
DEBUG_ERROR("Relay private key not found for cache preload");
return -1;
}
memset(g_cached_relay_privkey, 0, sizeof(g_cached_relay_privkey));
strncpy(g_cached_relay_privkey, private_key, sizeof(g_cached_relay_privkey) - 1);
g_cached_relay_privkey[sizeof(g_cached_relay_privkey) - 1] = '\0';
free(private_key);
return 0;
}
const char* get_temp_relay_private_key(void) {
if (strlen(g_temp_relay_privkey) == 64) {
return g_temp_relay_privkey;
@@ -1606,13 +1654,7 @@ int handle_configuration_event(cJSON* event, char* error_message, size_t error_s
// Note: Config table is now created via embedded schema in sql_schema.h
// Get value from config table
const char* get_config_value_from_table(const char* key) {
if (!db_is_available() || !key) {
return NULL;
}
return db_get_config_value_dup(key);
}
const char* get_config_value_from_table(const char* key);
// Set value in config table
int set_config_value_in_table(const char* key, const char* value, const char* data_type,
@@ -1621,7 +1663,12 @@ int set_config_value_in_table(const char* key, const char* value, const char* da
return -1;
}
return db_set_config_value_full(key, value, data_type, description, category, requires_restart);
int rc = db_set_config_value_full(key, value, data_type, description, category, requires_restart);
if (rc == 0) {
invalidate_config_cache();
}
return rc;
}
// Update config in table (simpler version of set_config_value_in_table)
@@ -1637,7 +1684,12 @@ int update_config_in_table(const char* key, const char* value) {
return -1;
}
return db_update_config_value_only(key, value);
int rc = db_update_config_value_only(key, value);
if (rc == 0) {
invalidate_config_cache();
}
return rc;
}
// DEPRECATED: This function is no longer used in the unified startup flow.
@@ -2067,7 +2119,7 @@ int wot_sync_from_admin_kind3(void) {
DEBUG_INFO("WoT sync: Starting sync from admin's kind 3 event (level %d)", wot_level);
sqlite3_stmt* stmt = NULL;
db_stmt_t* stmt = NULL;
int rc;
int whitelisted_count = 0;
@@ -2079,7 +2131,7 @@ int wot_sync_from_admin_kind3(void) {
"AND e.created_at = (SELECT MAX(created_at) FROM events WHERE kind = 3 AND pubkey = ?)";
rc = db_prepare(query_sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("WoT sync: Failed to prepare p-tag query: %s", db_last_error());
free((char*)admin_pubkey);
free((char*)relay_pubkey);
@@ -2103,7 +2155,7 @@ int wot_sync_from_admin_kind3(void) {
return -1;
}
while ((rc = db_step_stmt(stmt)) == SQLITE_ROW) {
while ((rc = db_step_stmt(stmt)) == DB_ROW) {
const char* p_tag = (const char*)db_column_text_value(stmt, 0);
if (p_tag) {
// Expand array if needed
@@ -2139,7 +2191,7 @@ int wot_sync_from_admin_kind3(void) {
// Step 2: Begin transaction
rc = db_exec_sql("BEGIN TRANSACTION");
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("WoT sync: Failed to begin transaction: %s", db_last_error());
for (int i = 0; i < p_tag_count; i++) {
free(p_tags[i]);
@@ -2170,7 +2222,7 @@ int wot_sync_from_admin_kind3(void) {
"VALUES ('wot_whitelist', 'pubkey', ?, 1)";
rc = db_prepare(insert_sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("WoT sync: Failed to prepare insert statement: %s", db_last_error());
db_exec_sql("ROLLBACK");
for (int i = 0; i < p_tag_count; i++) {
@@ -2186,7 +2238,7 @@ int wot_sync_from_admin_kind3(void) {
rc = db_step_stmt(stmt);
db_finalize_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("WoT sync: Failed to insert admin pubkey whitelist: %s", db_last_error());
db_exec_sql("ROLLBACK");
for (int i = 0; i < p_tag_count; i++) {
@@ -2202,7 +2254,7 @@ int wot_sync_from_admin_kind3(void) {
// Step 5: Insert relay pubkey as wot_whitelist (needed for DM responses)
rc = db_prepare(insert_sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("WoT sync: Failed to prepare insert statement for relay: %s", db_last_error());
db_exec_sql("ROLLBACK");
for (int i = 0; i < p_tag_count; i++) {
@@ -2218,7 +2270,7 @@ int wot_sync_from_admin_kind3(void) {
rc = db_step_stmt(stmt);
db_finalize_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("WoT sync: Failed to insert relay pubkey whitelist: %s", db_last_error());
db_exec_sql("ROLLBACK");
for (int i = 0; i < p_tag_count; i++) {
@@ -2235,7 +2287,7 @@ int wot_sync_from_admin_kind3(void) {
// Step 6: Insert each p tag from kind 3 event
for (int i = 0; i < p_tag_count; i++) {
rc = db_prepare(insert_sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("WoT sync: Failed to prepare insert for p_tag %d: %s", i, db_last_error());
db_exec_sql("ROLLBACK");
// Free remaining tags
@@ -2252,7 +2304,7 @@ int wot_sync_from_admin_kind3(void) {
rc = db_step_stmt(stmt);
db_finalize_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("WoT sync: Failed to insert p_tag %d: %s", i, db_last_error());
db_exec_sql("ROLLBACK");
// Free remaining tags
@@ -2269,7 +2321,7 @@ int wot_sync_from_admin_kind3(void) {
// Step 7: Commit transaction
rc = db_exec_sql("COMMIT");
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("WoT sync: Failed to commit transaction: %s", db_last_error());
db_exec_sql("ROLLBACK");
for (int i = 0; i < p_tag_count; i++) {
@@ -2973,9 +3025,9 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
}
// Execute query
sqlite3_stmt* stmt;
db_stmt_t* stmt;
int rc = db_prepare(sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
snprintf(error_message, error_size, "failed to prepare auth query");
return -1;
}
@@ -2995,7 +3047,7 @@ int handle_auth_query_unified(cJSON* event, const char* query_type, char* error_
int rule_count = 0;
// printf("=== Auth Query Results (%s) ===\n", query_type);
while (db_step_stmt(stmt) == SQLITE_ROW) {
while (db_step_stmt(stmt) == DB_ROW) {
const char* rule_type = (const char*)db_column_text_value(stmt, 0);
const char* pattern_type = (const char*)db_column_text_value(stmt, 1);
const char* pattern_value_result = (const char*)db_column_text_value(stmt, 2);
@@ -3091,9 +3143,9 @@ int handle_config_query_unified(cJSON* event, const char* query_type, char* erro
}
// Execute query
sqlite3_stmt* stmt;
db_stmt_t* stmt;
int rc = db_prepare(sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
snprintf(error_message, error_size, "failed to prepare config query");
return -1;
}
@@ -3112,7 +3164,7 @@ int handle_config_query_unified(cJSON* event, const char* query_type, char* erro
int config_count = 0;
while (db_step_stmt(stmt) == SQLITE_ROW) {
while (db_step_stmt(stmt) == DB_ROW) {
const char* key = (const char*)db_column_text_value(stmt, 0);
const char* value = (const char*)db_column_text_value(stmt, 1);
const char* data_type = (const char*)db_column_text_value(stmt, 2);
@@ -3680,9 +3732,9 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
cJSON_AddNumberToObject(response, "database_size_bytes", db_size);
// Query total events count
sqlite3_stmt* stmt;
if (db_prepare("SELECT COUNT(*) FROM events", &stmt) == SQLITE_OK) {
if (db_step_stmt(stmt) == SQLITE_ROW) {
db_stmt_t* stmt;
if (db_prepare("SELECT COUNT(*) FROM events", &stmt) == DB_OK) {
if (db_step_stmt(stmt) == DB_ROW) {
cJSON_AddNumberToObject(response, "total_events", db_column_int64_value(stmt, 0));
}
db_finalize_stmt(stmt);
@@ -3690,8 +3742,8 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
// Query event kinds distribution
cJSON* event_kinds = cJSON_CreateArray();
if (db_prepare("SELECT kind, count, percentage FROM event_kinds_view ORDER BY count DESC", &stmt) == SQLITE_OK) {
while (db_step_stmt(stmt) == SQLITE_ROW) {
if (db_prepare("SELECT kind, count, percentage FROM event_kinds_view ORDER BY count DESC", &stmt) == DB_OK) {
while (db_step_stmt(stmt) == DB_ROW) {
cJSON* kind_obj = cJSON_CreateObject();
cJSON_AddNumberToObject(kind_obj, "kind", db_column_int_value(stmt, 0));
cJSON_AddNumberToObject(kind_obj, "count", db_column_int64_value(stmt, 1));
@@ -3704,10 +3756,10 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
// Query time-based statistics
cJSON* time_stats = cJSON_CreateObject();
if (db_prepare("SELECT period, total_events FROM time_stats_view", &stmt) == SQLITE_OK) {
while (db_step_stmt(stmt) == SQLITE_ROW) {
if (db_prepare("SELECT period, total_events FROM time_stats_view", &stmt) == DB_OK) {
while (db_step_stmt(stmt) == DB_ROW) {
const char* period = (const char*)db_column_text_value(stmt, 0);
sqlite3_int64 count = db_column_int64_value(stmt, 1);
long long count = db_column_int64_value(stmt, 1);
if (strcmp(period, "total") == 0) {
cJSON_AddNumberToObject(time_stats, "total", count);
@@ -3725,8 +3777,8 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
// Query top pubkeys
cJSON* top_pubkeys = cJSON_CreateArray();
if (db_prepare("SELECT pubkey, event_count, percentage FROM top_pubkeys_view ORDER BY event_count DESC LIMIT 10", &stmt) == SQLITE_OK) {
while (db_step_stmt(stmt) == SQLITE_ROW) {
if (db_prepare("SELECT pubkey, event_count, percentage FROM top_pubkeys_view ORDER BY event_count DESC LIMIT 10", &stmt) == DB_OK) {
while (db_step_stmt(stmt) == DB_ROW) {
cJSON* pubkey_obj = cJSON_CreateObject();
const char* pubkey = (const char*)db_column_text_value(stmt, 0);
cJSON_AddStringToObject(pubkey_obj, "pubkey", pubkey ? pubkey : "");
@@ -3739,9 +3791,9 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
cJSON_AddItemToObject(response, "top_pubkeys", top_pubkeys);
// Get database creation timestamp (oldest event)
if (db_prepare("SELECT MIN(created_at) FROM events", &stmt) == SQLITE_OK) {
if (db_step_stmt(stmt) == SQLITE_ROW) {
sqlite3_int64 oldest_timestamp = db_column_int64_value(stmt, 0);
if (db_prepare("SELECT MIN(created_at) FROM events", &stmt) == DB_OK) {
if (db_step_stmt(stmt) == DB_ROW) {
long long oldest_timestamp = db_column_int64_value(stmt, 0);
if (oldest_timestamp > 0) {
cJSON_AddNumberToObject(response, "database_created_at", (double)oldest_timestamp);
}
@@ -3750,9 +3802,9 @@ int handle_stats_query_unified(cJSON* event, char* error_message, size_t error_s
}
// Get latest event timestamp
if (db_prepare("SELECT MAX(created_at) FROM events", &stmt) == SQLITE_OK) {
if (db_step_stmt(stmt) == SQLITE_ROW) {
sqlite3_int64 latest_timestamp = db_column_int64_value(stmt, 0);
if (db_prepare("SELECT MAX(created_at) FROM events", &stmt) == DB_OK) {
if (db_step_stmt(stmt) == DB_ROW) {
long long latest_timestamp = db_column_int64_value(stmt, 0);
if (latest_timestamp > 0) {
cJSON_AddNumberToObject(response, "latest_event_at", (double)latest_timestamp);
}
@@ -3884,7 +3936,7 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
// Begin transaction for atomic config updates
int rc = db_exec_sql("BEGIN IMMEDIATE TRANSACTION");
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
cJSON_Delete(config_objects_array);
snprintf(error_message, error_size, "failed to begin config update transaction");
return -1;
@@ -3960,10 +4012,10 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
// Check if the config key exists in the table
const char* check_sql = "SELECT COUNT(*) FROM config WHERE key = ?";
sqlite3_stmt* check_stmt;
db_stmt_t* check_stmt;
int check_rc = db_prepare(check_sql, &check_stmt);
if (check_rc != SQLITE_OK) {
if (check_rc != DB_OK) {
DEBUG_ERROR("Failed to prepare config existence check");
validation_errors++;
continue;
@@ -3972,7 +4024,7 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
db_bind_text_param(check_stmt, 1, key);
int config_exists = 0;
if (db_step_stmt(check_stmt) == SQLITE_ROW) {
if (db_step_stmt(check_stmt) == DB_ROW) {
config_exists = db_column_int_value(check_stmt, 0) > 0;
}
db_finalize_stmt(check_stmt);
@@ -3996,12 +4048,12 @@ int handle_config_update_unified(cJSON* event, char* error_message, size_t error
// Check if this config requires restart
const char* requires_restart_sql = "SELECT requires_restart FROM config WHERE key = ?";
sqlite3_stmt* restart_stmt;
db_stmt_t* restart_stmt;
int requires_restart = 0;
if (db_prepare(requires_restart_sql, &restart_stmt) == SQLITE_OK) {
if (db_prepare(requires_restart_sql, &restart_stmt) == DB_OK) {
db_bind_text_param(restart_stmt, 1, key);
if (db_step_stmt(restart_stmt) == SQLITE_ROW) {
if (db_step_stmt(restart_stmt) == DB_ROW) {
requires_restart = db_column_int_value(restart_stmt, 0);
}
db_finalize_stmt(restart_stmt);
@@ -4295,10 +4347,10 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
}
// Prepare INSERT OR REPLACE statement with all required fields
sqlite3_stmt* stmt = NULL;
db_stmt_t* stmt = NULL;
const char* sql = "INSERT OR REPLACE INTO config (key, value, data_type, description, category, requires_restart) VALUES (?, ?, ?, ?, ?, ?)";
rc = db_prepare(sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("Failed to prepare statement: %s", db_last_error());
db_exec_sql("ROLLBACK;");
return -1;
@@ -4378,7 +4430,7 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
db_bind_int_param(stmt, 6, requires_restart);
rc = db_step_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("Failed to insert config key '%s': %s",
key, db_last_error());
db_finalize_stmt(stmt);
@@ -4396,7 +4448,7 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
db_bind_text_param(stmt, 5, "authentication");
db_bind_int_param(stmt, 6, 0); // does not require restart
rc = db_step_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("Failed to insert admin_pubkey: %s", db_last_error());
db_finalize_stmt(stmt);
db_exec_sql("ROLLBACK;");
@@ -4412,7 +4464,7 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
db_bind_text_param(stmt, 5, "relay");
db_bind_int_param(stmt, 6, 0); // does not require restart
rc = db_step_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("Failed to insert relay_pubkey: %s", db_last_error());
db_finalize_stmt(stmt);
db_exec_sql("ROLLBACK;");
@@ -4429,7 +4481,7 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
db_bind_text_param(stmt, 5, "monitoring");
db_bind_int_param(stmt, 6, 0); // does not require restart
rc = db_step_stmt(stmt);
if (rc != SQLITE_DONE) {
if (rc != DB_DONE) {
DEBUG_ERROR("Failed to insert kind_24567_reporting_throttle_sec: %s", db_last_error());
db_finalize_stmt(stmt);
db_exec_sql("ROLLBACK;");
@@ -4454,6 +4506,159 @@ int populate_all_config_values_atomic(const char* admin_pubkey, const char* rela
// CONFIGURATION CACHE MANAGEMENT
// ================================
#define CONFIG_CACHE_MAX_ITEMS 256
typedef struct {
char key[128];
char value[CONFIG_VALUE_MAX_LENGTH + 1];
int in_use;
} config_cache_entry_t;
static config_cache_entry_t g_config_cache[CONFIG_CACHE_MAX_ITEMS];
static pthread_mutex_t g_config_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static int find_cache_index_by_key(const char* key) {
if (!key) {
return -1;
}
for (int i = 0; i < CONFIG_CACHE_MAX_ITEMS; i++) {
if (g_config_cache[i].in_use && strcmp(g_config_cache[i].key, key) == 0) {
return i;
}
}
return -1;
}
static int find_empty_cache_index(void) {
for (int i = 0; i < CONFIG_CACHE_MAX_ITEMS; i++) {
if (!g_config_cache[i].in_use) {
return i;
}
}
return -1;
}
static void clear_config_cache_locked(void) {
memset(g_config_cache, 0, sizeof(g_config_cache));
}
void invalidate_config_cache(void) {
pthread_mutex_lock(&g_config_cache_mutex);
clear_config_cache_locked();
pthread_mutex_unlock(&g_config_cache_mutex);
}
void force_config_cache_refresh(void) {
invalidate_config_cache();
}
int reload_config_from_table(void) {
force_config_cache_refresh();
if (!db_is_available()) {
return -1;
}
cJSON* rows = db_get_all_config_rows();
if (!rows || !cJSON_IsArray(rows)) {
if (rows) cJSON_Delete(rows);
return -1;
}
pthread_mutex_lock(&g_config_cache_mutex);
cJSON* row = NULL;
cJSON_ArrayForEach(row, rows) {
if (!cJSON_IsObject(row)) {
continue;
}
cJSON* key_obj = cJSON_GetObjectItemCaseSensitive(row, "key");
cJSON* value_obj = cJSON_GetObjectItemCaseSensitive(row, "value");
if (!cJSON_IsString(key_obj) || !cJSON_IsString(value_obj)) {
continue;
}
const char* key = cJSON_GetStringValue(key_obj);
const char* value = cJSON_GetStringValue(value_obj);
if (!key || !value) {
continue;
}
int idx = find_cache_index_by_key(key);
if (idx < 0) {
idx = find_empty_cache_index();
}
if (idx < 0) {
break;
}
g_config_cache[idx].in_use = 1;
strncpy(g_config_cache[idx].key, key, sizeof(g_config_cache[idx].key) - 1);
g_config_cache[idx].key[sizeof(g_config_cache[idx].key) - 1] = '\0';
strncpy(g_config_cache[idx].value, value, sizeof(g_config_cache[idx].value) - 1);
g_config_cache[idx].value[sizeof(g_config_cache[idx].value) - 1] = '\0';
}
pthread_mutex_unlock(&g_config_cache_mutex);
cJSON_Delete(rows);
return 0;
}
const char* get_config_value_from_table(const char* key) {
if (!key) {
return NULL;
}
pthread_mutex_lock(&g_config_cache_mutex);
int idx = find_cache_index_by_key(key);
if (idx >= 0) {
const char* result = strdup(g_config_cache[idx].value);
pthread_mutex_unlock(&g_config_cache_mutex);
return result;
}
pthread_mutex_unlock(&g_config_cache_mutex);
if (!db_is_available()) {
return NULL;
}
char* db_value = db_get_config_value_dup(key);
if (!db_value) {
return NULL;
}
pthread_mutex_lock(&g_config_cache_mutex);
idx = find_cache_index_by_key(key);
if (idx < 0) {
idx = find_empty_cache_index();
}
if (idx >= 0) {
g_config_cache[idx].in_use = 1;
strncpy(g_config_cache[idx].key, key, sizeof(g_config_cache[idx].key) - 1);
g_config_cache[idx].key[sizeof(g_config_cache[idx].key) - 1] = '\0';
strncpy(g_config_cache[idx].value, db_value, sizeof(g_config_cache[idx].value) - 1);
g_config_cache[idx].value[sizeof(g_config_cache[idx].value) - 1] = '\0';
}
pthread_mutex_unlock(&g_config_cache_mutex);
return db_value;
}
const char* get_admin_pubkey_cached(void) {
return get_config_value_from_table("admin_pubkey");
}
const char* get_relay_pubkey_cached(void) {
return get_config_value_from_table("relay_pubkey");
}
// ================================
// HYBRID CONFIG ACCESS FUNCTIONS
@@ -4483,15 +4688,15 @@ int is_config_table_ready(void) {
if (!db_is_available()) return 0;
const char* sql = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='config'";
sqlite3_stmt* stmt;
db_stmt_t* stmt;
int rc = db_prepare(sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
return 0;
}
int table_exists = 0;
if (db_step_stmt(stmt) == SQLITE_ROW) {
if (db_step_stmt(stmt) == DB_ROW) {
table_exists = db_column_int_value(stmt, 0) > 0;
}
db_finalize_stmt(stmt);
@@ -4503,12 +4708,12 @@ int is_config_table_ready(void) {
// Check if table has configuration data
const char* count_sql = "SELECT COUNT(*) FROM config";
rc = db_prepare(count_sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
return 0;
}
int config_count = 0;
if (db_step_stmt(stmt) == SQLITE_ROW) {
if (db_step_stmt(stmt) == DB_ROW) {
config_count = db_column_int_value(stmt, 0);
}
db_finalize_stmt(stmt);
@@ -4731,7 +4936,7 @@ int process_startup_config_event(const cJSON* event) {
// Begin transaction for atomic config updates
int rc = db_exec_sql("BEGIN IMMEDIATE TRANSACTION");
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("Failed to begin startup config transaction");
return -1;
}
@@ -4867,10 +5072,10 @@ cJSON* generate_config_event_from_table(void) {
// Query all configuration values from the config table
const char* sql = "SELECT key, value FROM config ORDER BY key";
sqlite3_stmt* stmt;
db_stmt_t* stmt;
int rc = db_prepare(sql, &stmt);
if (rc != SQLITE_OK) {
if (rc != DB_OK) {
DEBUG_ERROR("Failed to prepare config query for event generation");
cJSON_Delete(tags);
cJSON_Delete(event);
@@ -4880,7 +5085,7 @@ cJSON* generate_config_event_from_table(void) {
int config_items_added = 0;
// Add each config item as a tag
while (db_step_stmt(stmt) == SQLITE_ROW) {
while (db_step_stmt(stmt) == DB_ROW) {
const char* key = (const char*)db_column_text_value(stmt, 0);
const char* value = (const char*)db_column_text_value(stmt, 1);
+1 -1
View File
@@ -1,7 +1,6 @@
#ifndef CONFIG_H
#define CONFIG_H
#include <sqlite3.h>
#include <cjson/cJSON.h>
#include <time.h>
#include <pthread.h>
@@ -78,6 +77,7 @@ char* extract_pubkey_from_filename(const char* filename);
int store_relay_private_key(const char* relay_privkey_hex);
char* get_relay_private_key(void);
const char* get_temp_relay_private_key(void); // For first-time startup only
int preload_relay_private_key_cache(void); // Preload relay private key before strict DB mode
// NIP-42 authentication configuration functions
int parse_auth_required_kinds(const char* kinds_str, int* kinds_array, int max_kinds);
+272 -128
View File
@@ -4,6 +4,7 @@
#include "debug.h"
#include "config.h"
#include <sqlite3.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -12,87 +13,145 @@
extern sqlite3* g_db;
extern char g_database_path[512];
int db_is_available(void) {
return g_db != NULL;
// Optional per-thread connection override (used by thread pool workers)
static __thread sqlite3* g_thread_db = NULL;
static sqlite3* db_active_connection(void) {
return g_thread_db ? g_thread_db : g_db;
}
sqlite3* db_get_handle(void) {
return g_db;
typedef struct db_stmt {
sqlite3_stmt* stmt;
} db_stmt_t;
int db_init(const char* connection_string) {
if (!connection_string || connection_string[0] == '\0') return DB_MISUSE;
if (g_db) return DB_OK;
int rc = sqlite3_open(connection_string, &g_db);
if (rc != SQLITE_OK) {
if (g_db) {
sqlite3_close(g_db);
g_db = NULL;
}
return DB_ERROR;
}
strncpy(g_database_path, connection_string, sizeof(g_database_path) - 1);
g_database_path[sizeof(g_database_path) - 1] = '\0';
return DB_OK;
}
void db_close(void) {
if (!g_db) return;
sqlite3_close(g_db);
g_db = NULL;
}
int db_set_thread_connection(void* connection) {
g_thread_db = (sqlite3*)connection;
return DB_OK;
}
void db_clear_thread_connection(void) {
g_thread_db = NULL;
}
int db_is_available(void) {
return db_active_connection() != NULL;
}
const char* db_last_error(void) {
if (!g_db) return "database not available";
return sqlite3_errmsg(g_db);
sqlite3* db = db_active_connection();
if (!db) return "database not available";
return sqlite3_errmsg(db);
}
const char* db_get_database_path(void) {
return g_database_path;
}
int db_prepare(const char* sql, sqlite3_stmt** out_stmt) {
if (!g_db || !sql || !out_stmt) return SQLITE_MISUSE;
return sqlite3_prepare_v2(g_db, sql, -1, out_stmt, NULL);
int db_prepare(const char* sql, db_stmt_t** out_stmt) {
sqlite3* db = db_active_connection();
if (!db || !sql || !out_stmt) return DB_MISUSE;
sqlite3_stmt* raw_stmt = NULL;
int rc = sqlite3_prepare_v2(db, sql, -1, &raw_stmt, NULL);
if (rc != SQLITE_OK) return rc;
db_stmt_t* wrapper = (db_stmt_t*)malloc(sizeof(db_stmt_t));
if (!wrapper) {
sqlite3_finalize(raw_stmt);
return DB_ERROR;
}
wrapper->stmt = raw_stmt;
*out_stmt = wrapper;
return DB_OK;
}
int db_bind_text_param(sqlite3_stmt* stmt, int index, const char* value) {
if (!stmt) return SQLITE_MISUSE;
return sqlite3_bind_text(stmt, index, value ? value : "", -1, SQLITE_TRANSIENT);
int db_bind_text_param(db_stmt_t* stmt, int index, const char* value) {
if (!stmt || !stmt->stmt) return DB_MISUSE;
return sqlite3_bind_text(stmt->stmt, index, value ? value : "", -1, SQLITE_TRANSIENT);
}
int db_bind_int_param(sqlite3_stmt* stmt, int index, int value) {
if (!stmt) return SQLITE_MISUSE;
return sqlite3_bind_int(stmt, index, value);
int db_bind_int_param(db_stmt_t* stmt, int index, int value) {
if (!stmt || !stmt->stmt) return DB_MISUSE;
return sqlite3_bind_int(stmt->stmt, index, value);
}
int db_bind_int64_param(sqlite3_stmt* stmt, int index, long long value) {
if (!stmt) return SQLITE_MISUSE;
return sqlite3_bind_int64(stmt, index, (sqlite3_int64)value);
int db_bind_int64_param(db_stmt_t* stmt, int index, long long value) {
if (!stmt || !stmt->stmt) return DB_MISUSE;
return sqlite3_bind_int64(stmt->stmt, index, (sqlite3_int64)value);
}
int db_step_stmt(sqlite3_stmt* stmt) {
if (!stmt) return SQLITE_MISUSE;
return sqlite3_step(stmt);
int db_step_stmt(db_stmt_t* stmt) {
if (!stmt || !stmt->stmt) return DB_MISUSE;
return sqlite3_step(stmt->stmt);
}
int db_reset_stmt(sqlite3_stmt* stmt) {
if (!stmt) return SQLITE_MISUSE;
return sqlite3_reset(stmt);
int db_reset_stmt(db_stmt_t* stmt) {
if (!stmt || !stmt->stmt) return DB_MISUSE;
return sqlite3_reset(stmt->stmt);
}
const char* db_column_text_value(sqlite3_stmt* stmt, int col) {
if (!stmt) return NULL;
return (const char*)sqlite3_column_text(stmt, col);
const char* db_column_text_value(db_stmt_t* stmt, int col) {
if (!stmt || !stmt->stmt) return NULL;
return (const char*)sqlite3_column_text(stmt->stmt, col);
}
int db_column_int_value(sqlite3_stmt* stmt, int col) {
if (!stmt) return 0;
return sqlite3_column_int(stmt, col);
int db_column_int_value(db_stmt_t* stmt, int col) {
if (!stmt || !stmt->stmt) return 0;
return sqlite3_column_int(stmt->stmt, col);
}
long long db_column_int64_value(sqlite3_stmt* stmt, int col) {
if (!stmt) return 0;
return (long long)sqlite3_column_int64(stmt, col);
long long db_column_int64_value(db_stmt_t* stmt, int col) {
if (!stmt || !stmt->stmt) return 0;
return (long long)sqlite3_column_int64(stmt->stmt, col);
}
double db_column_double_value(sqlite3_stmt* stmt, int col) {
if (!stmt) return 0.0;
return sqlite3_column_double(stmt, col);
double db_column_double_value(db_stmt_t* stmt, int col) {
if (!stmt || !stmt->stmt) return 0.0;
return sqlite3_column_double(stmt->stmt, col);
}
void db_finalize_stmt(sqlite3_stmt* stmt) {
if (stmt) sqlite3_finalize(stmt);
void db_finalize_stmt(db_stmt_t* stmt) {
if (!stmt) return;
if (stmt->stmt) sqlite3_finalize(stmt->stmt);
free(stmt);
}
int db_log_subscription_created(const char* sub_id, const char* wsi_ptr,
const char* client_ip, const char* filter_json) {
if (!g_db || !sub_id || !wsi_ptr || !client_ip) return -1;
sqlite3* db = db_active_connection();
if (!db || !sub_id || !wsi_ptr || !client_ip) return -1;
const char* sql =
"INSERT OR REPLACE INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type, filter_json) "
"VALUES (?, ?, ?, 'created', ?)";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, sub_id, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, wsi_ptr, -1, SQLITE_TRANSIENT);
@@ -105,14 +164,15 @@ int db_log_subscription_created(const char* sub_id, const char* wsi_ptr,
}
int db_log_subscription_closed(const char* sub_id, const char* client_ip) {
if (!g_db || !sub_id) return -1;
sqlite3* db = db_active_connection();
if (!db || !sub_id) return -1;
const char* insert_sql =
"INSERT INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type) "
"VALUES (?, '', ?, 'closed')";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, insert_sql, -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, sub_id, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, client_ip ? client_ip : "unknown", -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
@@ -124,7 +184,7 @@ int db_log_subscription_closed(const char* sub_id, const char* client_ip) {
"SET ended_at = strftime('%s', 'now') "
"WHERE subscription_id = ? AND event_type = 'created' AND ended_at IS NULL";
if (sqlite3_prepare_v2(g_db, update_sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, update_sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, sub_id, -1, SQLITE_TRANSIENT);
int rc = sqlite3_step(stmt);
sqlite3_finalize(stmt);
@@ -133,7 +193,8 @@ int db_log_subscription_closed(const char* sub_id, const char* client_ip) {
}
int db_log_subscription_disconnected(const char* client_ip) {
if (!g_db || !client_ip) return -1;
sqlite3* db = db_active_connection();
if (!db || !client_ip) return -1;
const char* update_sql =
"UPDATE subscriptions "
@@ -141,11 +202,11 @@ int db_log_subscription_disconnected(const char* client_ip) {
"WHERE client_ip = ? AND event_type = 'created' AND ended_at IS NULL";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, update_sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, update_sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, client_ip, -1, SQLITE_TRANSIENT);
int rc = sqlite3_step(stmt);
int changes = sqlite3_changes(g_db);
int changes = sqlite3_changes(db);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE) return -1;
@@ -154,7 +215,7 @@ int db_log_subscription_disconnected(const char* client_ip) {
"INSERT INTO subscriptions (subscription_id, wsi_pointer, client_ip, event_type) "
"VALUES ('disconnect', '', ?, 'disconnected')";
if (sqlite3_prepare_v2(g_db, insert_sql, -1, &stmt, NULL) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL) == SQLITE_OK) {
sqlite3_bind_text(stmt, 1, client_ip, -1, SQLITE_TRANSIENT);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
@@ -165,7 +226,8 @@ int db_log_subscription_disconnected(const char* client_ip) {
}
int db_update_subscription_events_sent(const char* sub_id, int events_sent) {
if (!g_db || !sub_id) return -1;
sqlite3* db = db_active_connection();
if (!db || !sub_id) return -1;
const char* sql =
"UPDATE subscriptions "
@@ -173,7 +235,7 @@ int db_update_subscription_events_sent(const char* sub_id, int events_sent) {
"WHERE subscription_id = ? AND event_type = 'created'";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_int(stmt, 1, events_sent);
sqlite3_bind_text(stmt, 2, sub_id, -1, SQLITE_TRANSIENT);
@@ -184,7 +246,8 @@ int db_update_subscription_events_sent(const char* sub_id, int events_sent) {
}
int db_cleanup_orphaned_subscriptions(void) {
if (!g_db) return -1;
sqlite3* db = db_active_connection();
if (!db) return -1;
const char* sql =
"UPDATE subscriptions "
@@ -192,22 +255,23 @@ int db_cleanup_orphaned_subscriptions(void) {
"WHERE event_type = 'created' AND ended_at IS NULL";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
int rc = sqlite3_step(stmt);
int changes = sqlite3_changes(g_db);
int changes = sqlite3_changes(db);
sqlite3_finalize(stmt);
return (rc == SQLITE_DONE) ? changes : -1;
}
int db_get_event_pubkey(const char* event_id, char* pubkey_out, size_t pubkey_out_size) {
if (!g_db || !event_id || !pubkey_out || pubkey_out_size == 0) return -1;
sqlite3* db = db_active_connection();
if (!db || !event_id || !pubkey_out || pubkey_out_size == 0) return -1;
const char* sql = "SELECT pubkey FROM events WHERE id = ?";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, event_id, -1, SQLITE_TRANSIENT);
int rc = sqlite3_step(stmt);
@@ -225,18 +289,19 @@ int db_get_event_pubkey(const char* event_id, char* pubkey_out, size_t pubkey_ou
}
int db_delete_event_by_id(const char* event_id, const char* requester_pubkey) {
if (!g_db || !event_id || !requester_pubkey) return -1;
sqlite3* db = db_active_connection();
if (!db || !event_id || !requester_pubkey) return -1;
const char* sql = "DELETE FROM events WHERE id = ? AND pubkey = ?";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, event_id, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, requester_pubkey, -1, SQLITE_TRANSIENT);
int rc = sqlite3_step(stmt);
int changes = sqlite3_changes(g_db);
int changes = sqlite3_changes(db);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE) return -1;
@@ -245,7 +310,8 @@ int db_delete_event_by_id(const char* event_id, const char* requester_pubkey) {
int db_delete_events_by_address(const char* pubkey, int kind,
const char* d_tag, long before_timestamp) {
if (!g_db || !pubkey) return -1;
sqlite3* db = db_active_connection();
if (!db || !pubkey) return -1;
const char* sql_with_d =
"DELETE FROM events WHERE kind = ? AND pubkey = ? AND created_at <= ? "
@@ -256,7 +322,7 @@ int db_delete_events_by_address(const char* pubkey, int kind,
const int has_d = (d_tag && d_tag[0] != '\0');
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, has_d ? sql_with_d : sql_no_d, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, has_d ? sql_with_d : sql_no_d, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -268,7 +334,7 @@ int db_delete_events_by_address(const char* pubkey, int kind,
}
int rc = sqlite3_step(stmt);
int changes = sqlite3_changes(g_db);
int changes = sqlite3_changes(db);
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE) return -1;
@@ -356,10 +422,11 @@ int db_count_active_whitelist_rules(void) {
}
int db_count_with_sql(const char* sql, const char** bind_params, int bind_param_count, int* out_count) {
if (!g_db || !sql || !out_count) return -1;
sqlite3* db = db_active_connection();
if (!db || !sql || !out_count) return -1;
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -381,14 +448,15 @@ int db_count_with_sql(const char* sql, const char** bind_params, int bind_param_
char* db_execute_readonly_query_json(const char* query, const char* request_id,
char* error_message, size_t error_size,
int max_rows, int timeout_ms) {
if (!g_db || !query || !request_id || !error_message) return NULL;
sqlite3* db = db_active_connection();
if (!db || !query || !request_id || !error_message) return NULL;
sqlite3_busy_timeout(g_db, timeout_ms > 0 ? timeout_ms : 5000);
sqlite3_busy_timeout(db, timeout_ms > 0 ? timeout_ms : 5000);
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(g_db, query, -1, &stmt, NULL);
int rc = sqlite3_prepare_v2(db, query, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
const char* err_msg = sqlite3_errmsg(g_db);
const char* err_msg = sqlite3_errmsg(db);
snprintf(error_message, error_size, "SQL prepare failed: %s", err_msg ? err_msg : "unknown");
return NULL;
}
@@ -501,7 +569,7 @@ char* db_execute_readonly_query_json(const char* query, const char* request_id,
sqlite3_finalize(stmt);
if (rc != SQLITE_DONE && rc != SQLITE_ROW) {
const char* err_msg = sqlite3_errmsg(g_db);
const char* err_msg = sqlite3_errmsg(db);
snprintf(error_message, error_size, "SQL execution failed: %s", err_msg ? err_msg : "unknown");
cJSON_Delete(rows);
cJSON_Delete(response);
@@ -528,11 +596,12 @@ char* db_execute_readonly_query_json(const char* query, const char* request_id,
}
int db_get_total_event_count_ll(long long* out_count) {
if (!g_db || !out_count) return -1;
sqlite3* db = db_active_connection();
if (!db || !out_count) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT COUNT(*) FROM events";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
long long count = 0;
if (sqlite3_step(stmt) == SQLITE_ROW) {
@@ -545,11 +614,12 @@ int db_get_total_event_count_ll(long long* out_count) {
}
int db_get_event_count_since(time_t cutoff, long long* out_count) {
if (!g_db || !out_count) return -1;
sqlite3* db = db_active_connection();
if (!db || !out_count) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT COUNT(*) FROM events WHERE created_at >= ?";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)cutoff);
@@ -564,11 +634,12 @@ int db_get_event_count_since(time_t cutoff, long long* out_count) {
}
cJSON* db_get_event_kind_distribution_rows(long long* out_total_events) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT kind, COUNT(*) as count FROM events GROUP BY kind ORDER BY count DESC";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
cJSON* rows = cJSON_CreateArray();
if (!rows) {
@@ -602,11 +673,12 @@ cJSON* db_get_event_kind_distribution_rows(long long* out_total_events) {
}
cJSON* db_get_top_pubkeys_rows(int limit) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT pubkey, COUNT(*) as count FROM events GROUP BY pubkey ORDER BY count DESC LIMIT ?";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
sqlite3_bind_int(stmt, 1, limit > 0 ? limit : 10);
@@ -636,7 +708,8 @@ cJSON* db_get_top_pubkeys_rows(int limit) {
}
cJSON* db_get_subscription_details_rows(void) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
sqlite3_stmt* stmt = NULL;
const char* sql =
@@ -644,7 +717,7 @@ cJSON* db_get_subscription_details_rows(void) {
"FROM active_subscriptions_log "
"ORDER BY created_at DESC";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
cJSON* rows = cJSON_CreateArray();
if (!rows) {
@@ -684,11 +757,12 @@ cJSON* db_get_subscription_details_rows(void) {
}
cJSON* db_get_all_config_rows(void) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT key, value FROM config ORDER BY key";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
cJSON* rows = cJSON_CreateArray();
if (!rows) {
@@ -716,11 +790,12 @@ cJSON* db_get_all_config_rows(void) {
}
char* db_get_config_value_dup(const char* key) {
if (!g_db || !key) return NULL;
sqlite3* db = db_active_connection();
if (!db || !key) return NULL;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT value FROM config WHERE key = ?";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT);
@@ -736,14 +811,15 @@ char* db_get_config_value_dup(const char* key) {
int db_set_config_value_full(const char* key, const char* value, const char* data_type,
const char* description, const char* category, int requires_restart) {
if (!g_db || !key || !value || !data_type) return -1;
sqlite3* db = db_active_connection();
if (!db || !key || !value || !data_type) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql =
"INSERT OR REPLACE INTO config (key, value, data_type, description, category, requires_restart) "
"VALUES (?, ?, ?, ?, ?, ?)";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -760,11 +836,12 @@ int db_set_config_value_full(const char* key, const char* value, const char* dat
}
int db_update_config_value_only(const char* key, const char* value) {
if (!g_db || !key || !value) return -1;
sqlite3* db = db_active_connection();
if (!db || !key || !value) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "UPDATE config SET value = ?, updated_at = strftime('%s', 'now') WHERE key = ?";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -777,12 +854,13 @@ int db_update_config_value_only(const char* key, const char* value) {
}
int db_upsert_config_value(const char* key, const char* value, const char* data_type) {
if (!g_db || !key || !value || !data_type) return -1;
sqlite3* db = db_active_connection();
if (!db || !key || !value || !data_type) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "INSERT OR REPLACE INTO config (key, value, data_type) VALUES (?, ?, ?)";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -796,11 +874,12 @@ int db_upsert_config_value(const char* key, const char* value, const char* data_
}
int db_store_relay_private_key_hex(const char* relay_privkey_hex) {
if (!g_db || !relay_privkey_hex) return -1;
sqlite3* db = db_active_connection();
if (!db || !relay_privkey_hex) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "INSERT OR REPLACE INTO relay_seckey (private_key_hex) VALUES (?)";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, relay_privkey_hex, -1, SQLITE_TRANSIENT);
int rc = sqlite3_step(stmt);
@@ -810,11 +889,12 @@ int db_store_relay_private_key_hex(const char* relay_privkey_hex) {
}
char* db_get_relay_private_key_hex_dup(void) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT private_key_hex FROM relay_seckey";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return NULL;
char* result = NULL;
if (sqlite3_step(stmt) == SQLITE_ROW) {
@@ -827,7 +907,8 @@ char* db_get_relay_private_key_hex_dup(void) {
}
int db_store_config_event(const cJSON* event) {
if (!g_db || !event) return -1;
sqlite3* db = db_active_connection();
if (!db || !event) return -1;
cJSON* id_obj = cJSON_GetObjectItemCaseSensitive((cJSON*)event, "id");
cJSON* pubkey_obj = cJSON_GetObjectItemCaseSensitive((cJSON*)event, "pubkey");
@@ -845,7 +926,7 @@ int db_store_config_event(const cJSON* event) {
sqlite3_stmt* stmt = NULL;
const char* sql = "INSERT OR REPLACE INTO events (id, pubkey, created_at, kind, event_type, content, sig, tags) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
free(tags_str);
return -1;
}
@@ -866,11 +947,57 @@ int db_store_config_event(const cJSON* event) {
return (rc == SQLITE_DONE) ? 0 : -1;
}
static int db_insert_event_tags_json_with_db(sqlite3* db, const char* event_id, const char* tags_json) {
if (!db || !event_id || !tags_json) {
return -1;
}
cJSON* tags = cJSON_Parse(tags_json);
if (!tags || !cJSON_IsArray(tags)) {
if (tags) cJSON_Delete(tags);
return 0;
}
const char* sql = "INSERT INTO event_tags (event_id, tag_name, tag_value, tag_index) VALUES (?, ?, ?, ?)";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
cJSON_Delete(tags);
return -1;
}
int tag_index = 0;
cJSON* tag = NULL;
cJSON_ArrayForEach(tag, tags) {
if (cJSON_IsArray(tag) && cJSON_GetArraySize(tag) >= 2) {
cJSON* name = cJSON_GetArrayItem(tag, 0);
cJSON* value = cJSON_GetArrayItem(tag, 1);
if (cJSON_IsString(name) && cJSON_IsString(value)) {
sqlite3_reset(stmt);
sqlite3_clear_bindings(stmt);
sqlite3_bind_text(stmt, 1, event_id, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, cJSON_GetStringValue(name), -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, cJSON_GetStringValue(value), -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 4, tag_index);
if (sqlite3_step(stmt) != SQLITE_DONE) {
DEBUG_WARN("Failed to insert event tag for %s: %s", event_id, sqlite3_errmsg(db));
}
}
}
tag_index++;
}
sqlite3_finalize(stmt);
cJSON_Delete(tags);
return 0;
}
int db_insert_event_with_json(const char* id, const char* pubkey, long long created_at,
int kind, const char* event_type, const char* content,
const char* sig, const char* tags_json, const char* event_json,
int* out_step_rc, int* out_extended_errcode) {
if (!g_db || !id || !pubkey || !event_type || !content || !sig || !tags_json || !event_json) {
sqlite3* db = db_active_connection();
if (!db || !id || !pubkey || !event_type || !content || !sig || !tags_json || !event_json) {
return -1;
}
@@ -879,7 +1006,7 @@ int db_insert_event_with_json(const char* id, const char* pubkey, long long crea
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -894,9 +1021,13 @@ int db_insert_event_with_json(const char* id, const char* pubkey, long long crea
sqlite3_bind_text(stmt, 9, event_json, -1, SQLITE_TRANSIENT);
int step_rc = sqlite3_step(stmt);
int extended_errcode = sqlite3_extended_errcode(g_db);
int extended_errcode = sqlite3_extended_errcode(db);
sqlite3_finalize(stmt);
if (step_rc == SQLITE_DONE) {
(void)db_insert_event_tags_json_with_db(db, id, tags_json);
}
if (out_step_rc) {
*out_step_rc = step_rc;
}
@@ -908,11 +1039,12 @@ int db_insert_event_with_json(const char* id, const char* pubkey, long long crea
}
int db_get_event_time_bounds(long long* out_min_created_at, long long* out_max_created_at) {
if (!g_db || !out_min_created_at || !out_max_created_at) return -1;
sqlite3* db = db_active_connection();
if (!db || !out_min_created_at || !out_max_created_at) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT MIN(created_at), MAX(created_at) FROM events";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -930,11 +1062,12 @@ int db_get_event_time_bounds(long long* out_min_created_at, long long* out_max_c
}
int db_event_id_exists(const char* event_id, int* out_exists) {
if (!g_db || !event_id || !out_exists) return -1;
sqlite3* db = db_active_connection();
if (!db || !event_id || !out_exists) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT 1 FROM events WHERE id=? LIMIT 1";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, event_id, 64, SQLITE_STATIC);
int exists = (sqlite3_step(stmt) == SQLITE_ROW) ? 1 : 0;
@@ -945,13 +1078,14 @@ int db_event_id_exists(const char* event_id, int* out_exists) {
}
cJSON* db_retrieve_event_by_id(const char* event_id) {
if (!g_db || !event_id) return NULL;
sqlite3* db = db_active_connection();
if (!db || !event_id) return NULL;
const char* sql =
"SELECT id, pubkey, created_at, kind, content, sig, tags FROM events WHERE id = ?";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return NULL;
}
@@ -987,11 +1121,12 @@ cJSON* db_retrieve_event_by_id(const char* event_id) {
}
char* db_get_latest_event_pubkey_for_kind_dup(int kind) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
const char* sql = "SELECT pubkey FROM events WHERE kind = ? ORDER BY created_at DESC LIMIT 1";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return NULL;
}
@@ -1010,11 +1145,12 @@ char* db_get_latest_event_pubkey_for_kind_dup(int kind) {
}
int db_get_config_row_count(int* out_count) {
if (!g_db || !out_count) return -1;
sqlite3* db = db_active_connection();
if (!db || !out_count) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "SELECT COUNT(*) FROM config";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -1029,15 +1165,16 @@ int db_get_config_row_count(int* out_count) {
}
int db_store_event_tags_cjson(const char* event_id, const cJSON* tags) {
if (!g_db || !event_id || !tags || !cJSON_IsArray(tags)) {
sqlite3* db = db_active_connection();
if (!db || !event_id || !tags || !cJSON_IsArray(tags)) {
return 0; // Not an error if no tags
}
const char* sql = "INSERT INTO event_tags (event_id, tag_name, tag_value, tag_index) VALUES (?, ?, ?, ?)";
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
DEBUG_ERROR("Failed to prepare event_tags insert: %s", sqlite3_errmsg(g_db));
DEBUG_ERROR("Failed to prepare event_tags insert: %s", sqlite3_errmsg(db));
return -1;
}
@@ -1057,7 +1194,7 @@ int db_store_event_tags_cjson(const char* event_id, const cJSON* tags) {
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
DEBUG_ERROR("Failed to insert event tag: %s", sqlite3_errmsg(g_db));
DEBUG_ERROR("Failed to insert event tag: %s", sqlite3_errmsg(db));
}
}
}
@@ -1069,10 +1206,11 @@ int db_store_event_tags_cjson(const char* event_id, const cJSON* tags) {
}
int db_populate_event_tags_from_existing(void) {
if (!g_db) return -1;
sqlite3* db = db_active_connection();
if (!db) return -1;
sqlite3_stmt* check_stmt = NULL;
if (sqlite3_prepare_v2(g_db, "SELECT COUNT(*) FROM event_tags", -1, &check_stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, "SELECT COUNT(*) FROM event_tags", -1, &check_stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -1087,10 +1225,10 @@ int db_populate_event_tags_from_existing(void) {
const char* sql = "SELECT id, tags FROM events WHERE tags != '[]'";
sqlite3_stmt* stmt = NULL;
int rc = sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL);
int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) return -1;
sqlite3_exec(g_db, "BEGIN TRANSACTION", NULL, NULL, NULL);
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
int event_count = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
@@ -1108,18 +1246,19 @@ int db_populate_event_tags_from_existing(void) {
}
sqlite3_finalize(stmt);
sqlite3_exec(g_db, "COMMIT", NULL, NULL, NULL);
sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
DEBUG_INFO("Populated event_tags for %d events", event_count);
return 0;
}
int db_add_auth_rule(const char* rule_type, const char* pattern_type, const char* pattern_value) {
if (!g_db || !rule_type || !pattern_type || !pattern_value) return -1;
sqlite3* db = db_active_connection();
if (!db || !rule_type || !pattern_type || !pattern_value) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "INSERT INTO auth_rules (rule_type, pattern_type, pattern_value) VALUES (?, ?, ?)";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_TRANSIENT);
@@ -1131,11 +1270,12 @@ int db_add_auth_rule(const char* rule_type, const char* pattern_type, const char
}
int db_remove_auth_rule(const char* rule_type, const char* pattern_type, const char* pattern_value) {
if (!g_db || !rule_type || !pattern_type || !pattern_value) return -1;
sqlite3* db = db_active_connection();
if (!db || !rule_type || !pattern_type || !pattern_value) return -1;
sqlite3_stmt* stmt = NULL;
const char* sql = "DELETE FROM auth_rules WHERE rule_type = ? AND pattern_type = ? AND pattern_value = ?";
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) return -1;
sqlite3_bind_text(stmt, 1, rule_type, -1, SQLITE_TRANSIENT);
sqlite3_bind_text(stmt, 2, pattern_type, -1, SQLITE_TRANSIENT);
@@ -1147,15 +1287,16 @@ int db_remove_auth_rule(const char* rule_type, const char* pattern_type, const c
}
int db_delete_wot_whitelist_rules(void) {
if (!g_db) return -1;
sqlite3* db = db_active_connection();
if (!db) return -1;
const char* sql = "DELETE FROM auth_rules WHERE rule_type = 'wot_whitelist'";
int rc = sqlite3_exec(g_db, sql, NULL, NULL, NULL);
int rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
return (rc == SQLITE_OK) ? 0 : -1;
}
int db_count_wot_whitelist_rules(void) {
if (!g_db) return 0;
if (!db_is_available()) return 0;
const char* sql = "SELECT COUNT(*) FROM auth_rules WHERE rule_type = 'wot_whitelist' AND active = 1";
int count = 0;
@@ -1166,11 +1307,12 @@ int db_count_wot_whitelist_rules(void) {
}
int db_table_exists(const char* table_name, int* out_exists) {
if (!g_db || !table_name || !out_exists) return -1;
sqlite3* db = db_active_connection();
if (!db || !table_name || !out_exists) return -1;
const char* sql = "SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return -1;
}
@@ -1181,11 +1323,12 @@ int db_table_exists(const char* table_name, int* out_exists) {
}
char* db_get_schema_version_dup(void) {
if (!g_db) return NULL;
sqlite3* db = db_active_connection();
if (!db) return NULL;
const char* sql = "SELECT value FROM schema_info WHERE key = 'version'";
sqlite3_stmt* stmt = NULL;
if (sqlite3_prepare_v2(g_db, sql, -1, &stmt, NULL) != SQLITE_OK) {
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
return NULL;
}
@@ -1202,8 +1345,9 @@ char* db_get_schema_version_dup(void) {
}
int db_exec_sql(const char* sql) {
if (!g_db || !sql) return -1;
sqlite3* db = db_active_connection();
if (!db || !sql) return -1;
int rc = sqlite3_exec(g_db, sql, NULL, NULL, NULL);
int rc = sqlite3_exec(db, sql, NULL, NULL, NULL);
return (rc == SQLITE_OK) ? 0 : -1;
}
+27 -13
View File
@@ -3,27 +3,41 @@
#include <stddef.h>
#include <time.h>
#include <sqlite3.h>
#include <cjson/cJSON.h>
// Generic helpers
int db_init(const char* connection_string);
void db_close(void);
int db_is_available(void);
sqlite3* db_get_handle(void);
const char* db_last_error(void);
const char* db_get_database_path(void);
// Per-thread connection override (used by thread pool workers)
int db_set_thread_connection(void* connection);
void db_clear_thread_connection(void);
// DB result codes (backend-agnostic)
#define DB_OK 0
#define DB_ERROR 1
#define DB_CONSTRAINT 19
#define DB_MISUSE 21
#define DB_ROW 100
#define DB_DONE 101
typedef struct db_stmt db_stmt_t;
// Generic statement helpers (Phase 1 migration)
int db_prepare(const char* sql, sqlite3_stmt** out_stmt);
int db_bind_text_param(sqlite3_stmt* stmt, int index, const char* value);
int db_bind_int_param(sqlite3_stmt* stmt, int index, int value);
int db_bind_int64_param(sqlite3_stmt* stmt, int index, long long value);
int db_step_stmt(sqlite3_stmt* stmt);
int db_reset_stmt(sqlite3_stmt* stmt);
const char* db_column_text_value(sqlite3_stmt* stmt, int col);
int db_column_int_value(sqlite3_stmt* stmt, int col);
long long db_column_int64_value(sqlite3_stmt* stmt, int col);
double db_column_double_value(sqlite3_stmt* stmt, int col);
void db_finalize_stmt(sqlite3_stmt* stmt);
int db_prepare(const char* sql, db_stmt_t** out_stmt);
int db_bind_text_param(db_stmt_t* stmt, int index, const char* value);
int db_bind_int_param(db_stmt_t* stmt, int index, int value);
int db_bind_int64_param(db_stmt_t* stmt, int index, long long value);
int db_step_stmt(db_stmt_t* stmt);
int db_reset_stmt(db_stmt_t* stmt);
const char* db_column_text_value(db_stmt_t* stmt, int col);
int db_column_int_value(db_stmt_t* stmt, int col);
long long db_column_int64_value(db_stmt_t* stmt, int col);
double db_column_double_value(db_stmt_t* stmt, int col);
void db_finalize_stmt(db_stmt_t* stmt);
// Subscription logging
int db_log_subscription_created(const char* sub_id, const char* wsi_ptr,
File diff suppressed because one or more lines are too long
+72 -54
View File
@@ -3,6 +3,7 @@
#include "debug.h"
#include "config.h"
#include "db_ops.h"
#include "thread_pool.h"
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
@@ -102,8 +103,7 @@ void ip_ban_init(void) {
}
void ip_ban_load_from_db(void) {
sqlite3* db = db_get_handle();
if (!db || !g_initialized) return;
if (!db_is_available() || !g_initialized) return;
// Create table if it doesn't exist (handles existing databases)
const char* create_sql =
@@ -121,18 +121,16 @@ void ip_ban_load_from_db(void) {
" first_seen INTEGER NOT NULL DEFAULT 0,"
" updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))"
");";
char* err = NULL;
if (sqlite3_exec(db, create_sql, NULL, NULL, &err) != SQLITE_OK) {
DEBUG_ERROR("Failed to create ip_bans table: %s", err ? err : "unknown");
if (err) sqlite3_free(err);
if (db_exec_sql(create_sql) != 0) {
DEBUG_ERROR("Failed to create ip_bans table: %s", db_last_error());
return;
}
// Migration: Add idle_* columns if they don't exist (ignore errors if already exists)
sqlite3_exec(db, "ALTER TABLE ip_bans ADD COLUMN idle_failure_count INTEGER NOT NULL DEFAULT 0", NULL, NULL, NULL);
sqlite3_exec(db, "ALTER TABLE ip_bans ADD COLUMN idle_ban_count INTEGER NOT NULL DEFAULT 0", NULL, NULL, NULL);
sqlite3_exec(db, "ALTER TABLE ip_bans ADD COLUMN idle_banned_until INTEGER NOT NULL DEFAULT 0", NULL, NULL, NULL);
sqlite3_exec(db, "ALTER TABLE ip_bans ADD COLUMN idle_first_failure INTEGER NOT NULL DEFAULT 0", NULL, NULL, NULL);
(void)db_exec_sql("ALTER TABLE ip_bans ADD COLUMN idle_failure_count INTEGER NOT NULL DEFAULT 0");
(void)db_exec_sql("ALTER TABLE ip_bans ADD COLUMN idle_ban_count INTEGER NOT NULL DEFAULT 0");
(void)db_exec_sql("ALTER TABLE ip_bans ADD COLUMN idle_banned_until INTEGER NOT NULL DEFAULT 0");
(void)db_exec_sql("ALTER TABLE ip_bans ADD COLUMN idle_first_failure INTEGER NOT NULL DEFAULT 0");
const char* sql =
"SELECT ip, failure_count, ban_count, banned_until, first_failure,"
@@ -141,16 +139,16 @@ void ip_ban_load_from_db(void) {
" idle_failure_count, idle_ban_count, idle_banned_until, idle_first_failure"
" FROM ip_bans";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
db_stmt_t* stmt;
if (db_prepare(sql, &stmt) != DB_OK) {
DEBUG_ERROR("Failed to prepare ip_bans load query");
return;
}
int loaded = 0;
pthread_mutex_lock(&g_ban_mutex);
while (sqlite3_step(stmt) == SQLITE_ROW) {
const char* ip = (const char*)sqlite3_column_text(stmt, 0);
while (db_step_stmt(stmt) == DB_ROW) {
const char* ip = db_column_text_value(stmt, 0);
if (!ip) continue;
int idx = find_slot(ip);
@@ -160,25 +158,25 @@ void ip_ban_load_from_db(void) {
entry->state = IP_BAN_ACTIVE;
strncpy(entry->ip, ip, sizeof(entry->ip) - 1);
entry->ip[sizeof(entry->ip) - 1] = '\0';
entry->failure_count = sqlite3_column_int(stmt, 1);
entry->ban_count = sqlite3_column_int(stmt, 2);
entry->banned_until = (time_t)sqlite3_column_int64(stmt, 3);
entry->first_failure = (time_t)sqlite3_column_int64(stmt, 4);
entry->has_authed_successfully = sqlite3_column_int(stmt, 5);
entry->last_success_at = (time_t)sqlite3_column_int64(stmt, 6);
entry->total_connections = sqlite3_column_int(stmt, 7);
entry->total_failures = sqlite3_column_int(stmt, 8);
entry->total_successes = sqlite3_column_int(stmt, 9);
entry->first_seen = (time_t)sqlite3_column_int64(stmt, 10);
entry->failure_count = db_column_int_value(stmt, 1);
entry->ban_count = db_column_int_value(stmt, 2);
entry->banned_until = (time_t)db_column_int64_value(stmt, 3);
entry->first_failure = (time_t)db_column_int64_value(stmt, 4);
entry->has_authed_successfully = db_column_int_value(stmt, 5);
entry->last_success_at = (time_t)db_column_int64_value(stmt, 6);
entry->total_connections = db_column_int_value(stmt, 7);
entry->total_failures = db_column_int_value(stmt, 8);
entry->total_successes = db_column_int_value(stmt, 9);
entry->first_seen = (time_t)db_column_int64_value(stmt, 10);
// Load idle tracking fields (default to 0 if columns don't exist yet)
entry->idle_failure_count = sqlite3_column_int(stmt, 11);
entry->idle_ban_count = sqlite3_column_int(stmt, 12);
entry->idle_banned_until = (time_t)sqlite3_column_int64(stmt, 13);
entry->idle_first_failure = (time_t)sqlite3_column_int64(stmt, 14);
entry->idle_failure_count = db_column_int_value(stmt, 11);
entry->idle_ban_count = db_column_int_value(stmt, 12);
entry->idle_banned_until = (time_t)db_column_int64_value(stmt, 13);
entry->idle_first_failure = (time_t)db_column_int64_value(stmt, 14);
loaded++;
}
pthread_mutex_unlock(&g_ban_mutex);
sqlite3_finalize(stmt);
db_finalize_stmt(stmt);
// Count how many are still actively banned
time_t now = time(NULL);
@@ -196,8 +194,7 @@ void ip_ban_load_from_db(void) {
}
void ip_ban_save_to_db(void) {
sqlite3* db = db_get_handle();
if (!db || !g_initialized) return;
if (!db_is_available() || !g_initialized) return;
const char* upsert_sql =
"INSERT OR REPLACE INTO ip_bans"
@@ -207,13 +204,13 @@ void ip_ban_save_to_db(void) {
" idle_failure_count, idle_ban_count, idle_banned_until, idle_first_failure)"
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, strftime('%s', 'now'), ?, ?, ?, ?)";
sqlite3_stmt* stmt;
if (sqlite3_prepare_v2(db, upsert_sql, -1, &stmt, NULL) != SQLITE_OK) {
db_stmt_t* stmt;
if (db_prepare(upsert_sql, &stmt) != DB_OK) {
DEBUG_ERROR("Failed to prepare ip_bans save query");
return;
}
sqlite3_exec(db, "BEGIN TRANSACTION", NULL, NULL, NULL);
(void)db_exec_sql("BEGIN TRANSACTION");
int saved = 0;
pthread_mutex_lock(&g_ban_mutex);
@@ -221,25 +218,25 @@ void ip_ban_save_to_db(void) {
ip_ban_entry_t* entry = &g_ban_table[i];
if (entry->state != IP_BAN_ACTIVE) continue;
sqlite3_reset(stmt);
sqlite3_bind_text(stmt, 1, entry->ip, -1, SQLITE_STATIC);
sqlite3_bind_int(stmt, 2, entry->failure_count);
sqlite3_bind_int(stmt, 3, entry->ban_count);
sqlite3_bind_int64(stmt, 4, (sqlite3_int64)entry->banned_until);
sqlite3_bind_int64(stmt, 5, (sqlite3_int64)entry->first_failure);
sqlite3_bind_int(stmt, 6, entry->has_authed_successfully);
sqlite3_bind_int64(stmt, 7, (sqlite3_int64)entry->last_success_at);
sqlite3_bind_int(stmt, 8, entry->total_connections);
sqlite3_bind_int(stmt, 9, entry->total_failures);
sqlite3_bind_int(stmt, 10, entry->total_successes);
sqlite3_bind_int64(stmt, 11, (sqlite3_int64)entry->first_seen);
(void)db_reset_stmt(stmt);
db_bind_text_param(stmt, 1, entry->ip);
db_bind_int_param(stmt, 2, entry->failure_count);
db_bind_int_param(stmt, 3, entry->ban_count);
db_bind_int64_param(stmt, 4, (long long)entry->banned_until);
db_bind_int64_param(stmt, 5, (long long)entry->first_failure);
db_bind_int_param(stmt, 6, entry->has_authed_successfully);
db_bind_int64_param(stmt, 7, (long long)entry->last_success_at);
db_bind_int_param(stmt, 8, entry->total_connections);
db_bind_int_param(stmt, 9, entry->total_failures);
db_bind_int_param(stmt, 10, entry->total_successes);
db_bind_int64_param(stmt, 11, (long long)entry->first_seen);
// Idle tracking fields
sqlite3_bind_int(stmt, 12, entry->idle_failure_count);
sqlite3_bind_int(stmt, 13, entry->idle_ban_count);
sqlite3_bind_int64(stmt, 14, (sqlite3_int64)entry->idle_banned_until);
sqlite3_bind_int64(stmt, 15, (sqlite3_int64)entry->idle_first_failure);
db_bind_int_param(stmt, 12, entry->idle_failure_count);
db_bind_int_param(stmt, 13, entry->idle_ban_count);
db_bind_int64_param(stmt, 14, (long long)entry->idle_banned_until);
db_bind_int64_param(stmt, 15, (long long)entry->idle_first_failure);
if (sqlite3_step(stmt) != SQLITE_DONE) {
if (db_step_stmt(stmt) != DB_DONE) {
DEBUG_WARN("Failed to save ip_ban entry for %s", entry->ip);
} else {
saved++;
@@ -247,14 +244,23 @@ void ip_ban_save_to_db(void) {
}
pthread_mutex_unlock(&g_ban_mutex);
sqlite3_finalize(stmt);
sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
db_finalize_stmt(stmt);
(void)db_exec_sql("COMMIT");
DEBUG_TRACE("IP ban table saved: %d entries written to DB", saved);
}
// Check if an IP is in the idle_ban_whitelist config (comma-separated list)
static int ip_is_whitelisted(const char* ip) {
if (!ip) {
return 0;
}
// Always trust loopback to avoid local test/dev self-bans.
if (strcmp(ip, "127.0.0.1") == 0 || strcmp(ip, "::1") == 0) {
return 1;
}
const char* whitelist = get_config_value("idle_ban_whitelist");
if (!whitelist || whitelist[0] == '\0') {
if (whitelist) free((char*)whitelist);
@@ -560,7 +566,19 @@ void ip_ban_log_stats(void) {
last_log = now;
// Save to DB every 5 minutes
ip_ban_save_to_db();
if (thread_pool_is_running()) {
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_IP_BAN_SAVE;
thread_pool_status_t rc = thread_pool_submit_write(&job, NULL);
if (rc != THREAD_POOL_STATUS_OK) {
DEBUG_WARN("Failed to queue IP_BAN_SAVE job (%d); saving synchronously", rc);
ip_ban_save_to_db();
}
} else {
ip_ban_save_to_db();
}
pthread_mutex_lock(&g_ban_mutex);
int auth_banned_count = 0;
+470 -184
View File
@@ -115,6 +115,12 @@ int process_admin_event_in_config(cJSON* event, char* error_message, size_t erro
// Forward declaration for NIP-45 COUNT message handling
int handle_count_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss);
// Forward declaration for NOTICE message support
void send_notice_message(struct lws* wsi, struct per_session_data* pss, const char* message);
// Thread pool wake callback (called by worker threads)
static void wake_event_loop_from_thread_pool(void* ctx);
// Parameter binding helpers for SQL queries
static void add_bind_param(char*** params, int* count, int* capacity, const char* value) {
if (*count >= *capacity) {
@@ -131,11 +137,308 @@ static void free_bind_params(char** params, int count) {
free(params);
}
typedef struct req_async_state {
char sub_id[SUBSCRIPTION_ID_MAX_LENGTH];
struct lws* wsi_token;
pthread_mutex_t mutex;
int pending_jobs;
} req_async_state_t;
typedef struct req_async_submit_ctx {
req_async_state_t* state;
} req_async_submit_ctx_t;
typedef struct req_async_completion {
req_async_state_t* state;
thread_pool_status_t status;
thread_pool_req_result_t* req_result;
struct req_async_completion* next;
} req_async_completion_t;
static pthread_mutex_t g_req_async_completion_mutex = PTHREAD_MUTEX_INITIALIZER;
static req_async_completion_t* g_req_async_completion_head = NULL;
static req_async_completion_t* g_req_async_completion_tail = NULL;
static void free_req_payload_main(void* p) {
thread_pool_req_payload_t* payload = (thread_pool_req_payload_t*)p;
if (!payload) return;
free(payload->sql);
if (payload->bind_params) {
for (int i = 0; i < payload->bind_param_count; i++) {
free(payload->bind_params[i]);
}
free(payload->bind_params);
}
free(payload);
}
static req_async_state_t* req_async_state_create(const char* sub_id, struct lws* wsi) {
req_async_state_t* state = calloc(1, sizeof(*state));
if (!state) {
return NULL;
}
if (sub_id) {
strncpy(state->sub_id, sub_id, sizeof(state->sub_id) - 1);
state->sub_id[sizeof(state->sub_id) - 1] = '\0';
}
state->wsi_token = wsi;
pthread_mutex_init(&state->mutex, NULL);
return state;
}
static void req_async_state_free(req_async_state_t* state) {
if (!state) return;
pthread_mutex_destroy(&state->mutex);
free(state);
}
static void req_async_state_increment_pending(req_async_state_t* state) {
if (!state) return;
pthread_mutex_lock(&state->mutex);
state->pending_jobs++;
pthread_mutex_unlock(&state->mutex);
}
static int req_async_state_decrement_pending(req_async_state_t* state) {
if (!state) return 0;
pthread_mutex_lock(&state->mutex);
if (state->pending_jobs > 0) {
state->pending_jobs--;
}
int remaining = state->pending_jobs;
pthread_mutex_unlock(&state->mutex);
return remaining;
}
static void req_async_completion_push(req_async_completion_t* completion) {
if (!completion) return;
pthread_mutex_lock(&g_req_async_completion_mutex);
completion->next = NULL;
if (!g_req_async_completion_tail) {
g_req_async_completion_head = completion;
g_req_async_completion_tail = completion;
} else {
g_req_async_completion_tail->next = completion;
g_req_async_completion_tail = completion;
}
pthread_mutex_unlock(&g_req_async_completion_mutex);
}
static req_async_completion_t* req_async_completion_pop(void) {
pthread_mutex_lock(&g_req_async_completion_mutex);
req_async_completion_t* completion = g_req_async_completion_head;
if (completion) {
g_req_async_completion_head = completion->next;
if (!g_req_async_completion_head) {
g_req_async_completion_tail = NULL;
}
}
pthread_mutex_unlock(&g_req_async_completion_mutex);
return completion;
}
static int resolve_req_async_target(req_async_state_t* state, struct lws** out_wsi, struct per_session_data** out_pss) {
if (!state || !out_wsi || !out_pss) {
return 0;
}
*out_wsi = NULL;
*out_pss = NULL;
pthread_mutex_lock(&g_subscription_manager.subscriptions_lock);
subscription_t* sub = g_subscription_manager.active_subscriptions;
while (sub) {
if (sub->active && sub->wsi == state->wsi_token && strcmp(sub->id, state->sub_id) == 0) {
*out_wsi = sub->wsi;
break;
}
sub = sub->next;
}
pthread_mutex_unlock(&g_subscription_manager.subscriptions_lock);
if (!*out_wsi) {
return 0;
}
*out_pss = (struct per_session_data*)lws_wsi_user(*out_wsi);
return (*out_pss != NULL);
}
static void send_eose_message(struct lws* wsi, struct per_session_data* pss, const char* sub_id) {
if (!wsi || !pss || !sub_id) {
return;
}
cJSON* eose_response = cJSON_CreateArray();
if (!eose_response) {
return;
}
cJSON_AddItemToArray(eose_response, cJSON_CreateString("EOSE"));
cJSON_AddItemToArray(eose_response, cJSON_CreateString(sub_id));
char *eose_str = cJSON_Print(eose_response);
if (eose_str) {
size_t eose_len = strlen(eose_str);
DEBUG_TRACE("WS_FRAME_SEND: type=EOSE len=%zu data=%.100s%s",
eose_len,
eose_str,
eose_len > 100 ? "..." : "");
if (queue_message(wsi, pss, eose_str, eose_len, LWS_WRITE_TEXT) != 0) {
DEBUG_ERROR("Failed to queue EOSE message");
}
free(eose_str);
}
cJSON_Delete(eose_response);
}
static void req_async_result_cb(const thread_pool_result_t* result, void* user_ctx) {
req_async_submit_ctx_t* ctx = (req_async_submit_ctx_t*)user_ctx;
if (!ctx || !ctx->state || !result) {
if (result && result->result_data) {
thread_pool_free_req_result((thread_pool_req_result_t*)result->result_data);
}
if (ctx) free(ctx);
return;
}
req_async_completion_t* completion = calloc(1, sizeof(*completion));
if (!completion) {
if (result->result_data) {
thread_pool_free_req_result((thread_pool_req_result_t*)result->result_data);
}
free(ctx);
return;
}
completion->state = ctx->state;
completion->status = result->status;
completion->req_result = (thread_pool_req_result_t*)result->result_data;
req_async_completion_push(completion);
wake_event_loop_from_thread_pool(NULL);
free(ctx);
}
static int submit_req_query_async(req_async_state_t* state, const char* sql, const char** bind_params, int bind_param_count) {
if (!state || !sql) {
return -1;
}
thread_pool_req_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) {
return -1;
}
payload->sql = strdup(sql);
payload->bind_param_count = bind_param_count;
if (!payload->sql) {
free_req_payload_main(payload);
return -1;
}
if (bind_param_count > 0) {
payload->bind_params = calloc((size_t)bind_param_count, sizeof(char*));
if (!payload->bind_params) {
free_req_payload_main(payload);
return -1;
}
for (int i = 0; i < bind_param_count; i++) {
const char* v = (bind_params && bind_params[i]) ? bind_params[i] : "";
payload->bind_params[i] = strdup(v);
if (!payload->bind_params[i]) {
free_req_payload_main(payload);
return -1;
}
}
}
req_async_submit_ctx_t* ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
free_req_payload_main(payload);
return -1;
}
ctx->state = state;
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_REQ_QUERY;
job.payload = payload;
job.payload_size = sizeof(*payload);
job.payload_free = free_req_payload_main;
job.result_cb = req_async_result_cb;
job.result_cb_ctx = ctx;
req_async_state_increment_pending(state);
thread_pool_status_t submit_rc = thread_pool_submit_read(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
req_async_state_decrement_pending(state);
free(ctx);
free_req_payload_main(payload);
return -1;
}
return 0;
}
void process_req_async_completions(void) {
req_async_completion_t* completion = NULL;
while ((completion = req_async_completion_pop()) != NULL) {
struct lws* target_wsi = NULL;
struct per_session_data* target_pss = NULL;
int has_target = resolve_req_async_target(completion->state, &target_wsi, &target_pss);
if (has_target && completion->status == THREAD_POOL_STATUS_OK && completion->req_result) {
for (int r = 0; r < completion->req_result->row_count; r++) {
const char* event_json_str = completion->req_result->event_json_rows[r];
if (!event_json_str) {
continue;
}
size_t sub_id_len = strlen(completion->state->sub_id);
size_t event_json_len = strlen(event_json_str);
size_t msg_len = 10 + sub_id_len + 3 + event_json_len + 1;
unsigned char* buf = malloc(LWS_PRE + msg_len + 1);
if (!buf) {
continue;
}
char* msg_ptr = (char*)(buf + LWS_PRE);
snprintf(msg_ptr, msg_len + 1, "[\"EVENT\",\"%s\",%s]", completion->state->sub_id, event_json_str);
size_t actual_len = strlen(msg_ptr);
if (queue_message_take_ownership(target_wsi, target_pss, buf, actual_len, LWS_WRITE_TEXT) != 0) {
DEBUG_ERROR("Failed to queue async EVENT message for sub=%s", completion->state->sub_id);
}
}
} else if (has_target && completion->status != THREAD_POOL_STATUS_OK) {
send_notice_message(target_wsi, target_pss, "error: failed to execute subscription query");
}
int remaining = req_async_state_decrement_pending(completion->state);
if (remaining == 0) {
if (has_target) {
send_eose_message(target_wsi, target_pss, completion->state->sub_id);
}
req_async_state_free(completion->state);
}
if (completion->req_result) {
thread_pool_free_req_result(completion->req_result);
}
free(completion);
}
}
// Forward declaration for enhanced admin event authorization
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, 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);
@@ -164,8 +467,6 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header);
// Forward declaration for WebSocket relay server
int start_websocket_relay(int port_override, int strict_port);
// Thread pool wake callback (called by worker threads)
static void wake_event_loop_from_thread_pool(void* ctx);
// Forward declarations for IP ban system
void ip_ban_init(void);
@@ -425,8 +726,8 @@ int init_database(const char* database_path_override) {
// Clean up stale WAL files before opening database
cleanup_stale_wal_files(db_path);
int rc = sqlite3_open(db_path, &g_db);
if (rc != SQLITE_OK) {
int rc = db_init(db_path);
if (rc != DB_OK) {
DEBUG_ERROR("Cannot open database");
DEBUG_TRACE("Exiting init_database() - failed to open database");
return -1;
@@ -437,7 +738,7 @@ int init_database(const char* database_path_override) {
// Check config table row count immediately after database open
int row_count = 0;
if (db_get_config_row_count(&row_count) == 0) {
DEBUG_LOG("Config table row count immediately after sqlite3_open(): %d", row_count);
DEBUG_LOG("Config table row count immediately after db_init(): %d", row_count);
} else {
DEBUG_LOG("Config table count unavailable immediately after sqlite3_open() (table may not exist yet)");
}
@@ -600,8 +901,7 @@ void close_database() {
DEBUG_WARN("WAL checkpoint warning");
}
sqlite3_close(g_db);
g_db = NULL;
db_close();
DEBUG_LOG("Database connection closed");
}
@@ -675,16 +975,16 @@ int store_event_tags(const char* event_id, cJSON* tags) {
return db_store_event_tags_cjson(event_id, tags);
}
// Store event in database
int store_event(cJSON* event) {
if (thread_pool_is_running()) {
DEBUG_TRACE("Thread pool scaffold active: store_event() still using synchronous DB path");
}
if (!g_db || !event) {
// Core event storage path.
// Returns:
// 0 = inserted into DB
// 1 = handled without insert (duplicate or ephemeral)
// -1 = failure
int store_event_core(cJSON* event) {
if (!db_is_available() || !event) {
return -1;
}
// Extract event fields
cJSON* id = cJSON_GetObjectItemCaseSensitive(event, "id");
cJSON* pubkey = cJSON_GetObjectItemCaseSensitive(event, "pubkey");
@@ -693,12 +993,12 @@ int store_event(cJSON* event) {
cJSON* content = cJSON_GetObjectItemCaseSensitive(event, "content");
cJSON* sig = cJSON_GetObjectItemCaseSensitive(event, "sig");
cJSON* tags = cJSON_GetObjectItemCaseSensitive(event, "tags");
if (!id || !pubkey || !created_at || !kind || !content || !sig) {
DEBUG_ERROR("Invalid event - missing required fields");
return -1;
}
// Classify event type
event_type_t type = classify_event_kind((int)cJSON_GetNumberValue(kind));
@@ -706,7 +1006,7 @@ int store_event(cJSON* event) {
if (type == EVENT_TYPE_EPHEMERAL) {
DEBUG_LOG("Ephemeral event (kind %d) - broadcasting only, not storing",
(int)cJSON_GetNumberValue(kind));
return 0; // Success - event was handled but not stored
return 1;
}
// Serialize tags to JSON (use empty array if no tags)
@@ -716,12 +1016,12 @@ int store_event(cJSON* event) {
} else {
tags_json = strdup("[]");
}
if (!tags_json) {
DEBUG_ERROR("Failed to serialize tags to JSON");
return -1;
}
// Serialize full event JSON for fast retrieval (use PrintUnformatted for compact storage)
char* event_json = cJSON_PrintUnformatted(event);
if (!event_json) {
@@ -729,35 +1029,40 @@ int store_event(cJSON* event) {
free(tags_json);
return -1;
}
int rc = SQLITE_ERROR;
int extended_errcode = 0;
if (db_insert_event_with_json(cJSON_GetStringValue(id),
cJSON_GetStringValue(pubkey),
(long long)cJSON_GetNumberValue(created_at),
(int)cJSON_GetNumberValue(kind),
event_type_to_string(type),
cJSON_GetStringValue(content),
cJSON_GetStringValue(sig),
tags_json,
event_json,
&rc,
&extended_errcode) != 0) {
thread_pool_store_event_payload_t payload;
memset(&payload, 0, sizeof(payload));
payload.id = (char*)cJSON_GetStringValue(id);
payload.pubkey = (char*)cJSON_GetStringValue(pubkey);
payload.created_at = (long long)cJSON_GetNumberValue(created_at);
payload.kind = (int)cJSON_GetNumberValue(kind);
payload.event_type = (char*)event_type_to_string(type);
payload.content = (char*)cJSON_GetStringValue(content);
payload.sig = (char*)cJSON_GetStringValue(sig);
payload.tags_json = tags_json;
payload.event_json = event_json;
thread_pool_store_event_result_t tp_result;
memset(&tp_result, 0, sizeof(tp_result));
if (thread_pool_execute_store_event_sync(&payload, &tp_result) != 0) {
DEBUG_ERROR("Failed to execute event insert operation");
free(tags_json);
free(event_json);
return -1;
}
if (rc != SQLITE_DONE) {
int rc = tp_result.step_rc;
int extended_errcode = tp_result.extended_errcode;
if (rc != DB_DONE) {
const char* err_msg = db_last_error();
if (rc != SQLITE_CONSTRAINT) {
if (rc != DB_CONSTRAINT) {
DEBUG_ERROR("INSERT failed: rc=%d, extended_errcode=%d, msg=%s", rc, extended_errcode, err_msg);
}
}
if (rc != SQLITE_DONE) {
if (rc == SQLITE_CONSTRAINT) {
if (rc != DB_DONE) {
if (rc == DB_CONSTRAINT) {
DEBUG_WARN("Event already exists in database");
// Add TRACE level debug to show both events
@@ -781,7 +1086,7 @@ int store_event(cJSON* event) {
free(tags_json);
free(event_json);
return 0; // Not an error, just duplicate
return 1;
}
char error_msg[256];
snprintf(error_msg, sizeof(error_msg), "Failed to insert event: %s", db_last_error());
@@ -793,13 +1098,18 @@ int store_event(cJSON* event) {
free(tags_json);
free(event_json);
return 0;
}
// Main-thread-only post-store follow-up actions.
void store_event_post_actions(cJSON* event) {
if (!event) {
return;
}
// Call monitoring hook after successful event storage
monitoring_on_event_stored();
// After successful event storage, insert denormalized tags
store_event_tags(cJSON_GetStringValue(id), tags);
// Check if this is a kind 3 event from the admin — trigger WoT sync
cJSON* kind_obj = cJSON_GetObjectItemCaseSensitive(event, "kind");
cJSON* pubkey_obj = cJSON_GetObjectItemCaseSensitive(event, "pubkey");
@@ -815,7 +1125,17 @@ int store_event(cJSON* event) {
if (admin_pubkey) free((char*)admin_pubkey);
}
}
}
// Backward-compatible wrapper for synchronous call sites.
int store_event(cJSON* event) {
int core_rc = store_event_core(event);
if (core_rc < 0) {
return -1;
}
if (core_rc == 0) {
store_event_post_actions(event);
}
return 0;
}
@@ -823,7 +1143,7 @@ int store_event(cJSON* event) {
// Uses the primary key index — single B-tree lookup, ~10μs.
// Call this BEFORE signature verification to skip expensive crypto on duplicates.
int event_id_exists_in_db(const char* event_id) {
if (!g_db || !event_id || strlen(event_id) != 64) return 0;
if (!db_is_available() || !event_id || strlen(event_id) != 64) return 0;
int exists = 0;
if (db_event_id_exists(event_id, &exists) != 0) {
@@ -901,10 +1221,6 @@ static int is_only_kind_99999_request(cJSON* filters) {
}
int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, struct per_session_data *pss) {
if (thread_pool_is_running()) {
DEBUG_TRACE("Thread pool scaffold active: handle_req_message() still using synchronous DB path");
}
if (!cJSON_IsArray(filters)) {
DEBUG_ERROR("REQ filters is not an array");
return 0;
@@ -1104,7 +1420,18 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
}
int events_sent = config_events_sent; // Start with synthetic config events
int submitted_jobs = 0;
int expiration_enabled = get_config_bool("expiration_enabled", 1);
int filter_responses = get_config_bool("expiration_filter", 1);
time_t query_now = time(NULL);
req_async_state_t* async_state = req_async_state_create(sub_id, wsi);
if (!async_state) {
DEBUG_ERROR("Failed to allocate async REQ state");
free_bind_params(bind_params, bind_param_count);
return events_sent;
}
// Process each filter in the array
for (int i = 0; i < cJSON_GetArraySize(filters); i++) {
cJSON* filter = cJSON_GetArrayItem(filters, i);
@@ -1128,13 +1455,26 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
// Build SQL query based on filter - exclude ephemeral events (kinds 20000-29999) from historical queries
// Select event_json for fast retrieval (no JSON reconstruction needed)
char sql[1024] = "SELECT event_json FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000)";
char sql[1408] = "SELECT event_json FROM events WHERE 1=1 AND (kind < 20000 OR kind >= 30000)";
char* sql_ptr = sql + strlen(sql);
int remaining = sizeof(sql) - strlen(sql);
// Note: Expiration filtering will be done at application level
// after retrieving events to ensure compatibility with all SQLite versions
// Phase 3: push expiration filtering into SQL using indexed event_tags table.
if (expiration_enabled && filter_responses) {
snprintf(sql_ptr, remaining,
" AND NOT EXISTS (SELECT 1 FROM event_tags et_exp "
"WHERE et_exp.event_id = events.id "
"AND et_exp.tag_name = ? "
"AND CAST(et_exp.tag_value AS INTEGER) <= ?)");
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, "expiration");
char now_buf[32];
snprintf(now_buf, sizeof(now_buf), "%lld", (long long)query_now);
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, now_buf);
}
// Handle kinds filter
cJSON* kinds = cJSON_GetObjectItemCaseSensitive(filter, "kinds");
if (kinds && cJSON_IsArray(kinds)) {
@@ -1143,7 +1483,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
snprintf(sql_ptr, remaining, " AND kind IN (");
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
for (int k = 0; k < kind_count; k++) {
cJSON* kind = cJSON_GetArrayItem(kinds, k);
if (cJSON_IsNumber(kind)) {
@@ -1162,7 +1502,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
remaining = sizeof(sql) - strlen(sql);
}
}
// Handle authors filter
cJSON* authors = cJSON_GetObjectItemCaseSensitive(filter, "authors");
if (authors && cJSON_IsArray(authors)) {
@@ -1208,8 +1548,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
if (ids && cJSON_IsArray(ids)) {
int id_count = 0;
// Count valid ids
for (int i = 0; i < cJSON_GetArraySize(ids); i++) {
cJSON* id = cJSON_GetArrayItem(ids, i);
for (int j = 0; j < cJSON_GetArraySize(ids); j++) {
cJSON* id = cJSON_GetArrayItem(ids, j);
if (cJSON_IsString(id)) {
id_count++;
}
@@ -1219,8 +1559,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
for (int i = 0; i < id_count; i++) {
if (i > 0) {
for (int j = 0; j < id_count; j++) {
if (j > 0) {
snprintf(sql_ptr, remaining, ",");
sql_ptr++;
remaining--;
@@ -1234,8 +1574,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
remaining = sizeof(sql) - strlen(sql);
// Add id values to bind params
for (int i = 0; i < cJSON_GetArraySize(ids); i++) {
cJSON* id = cJSON_GetArrayItem(ids, i);
for (int j = 0; j < cJSON_GetArraySize(ids); j++) {
cJSON* id = cJSON_GetArrayItem(ids, j);
if (cJSON_IsString(id)) {
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, cJSON_GetStringValue(id));
}
@@ -1254,8 +1594,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
if (cJSON_IsArray(filter_item)) {
int tag_value_count = 0;
// Count valid tag values
for (int i = 0; i < cJSON_GetArraySize(filter_item); i++) {
cJSON* tag_value = cJSON_GetArrayItem(filter_item, i);
for (int j = 0; j < cJSON_GetArraySize(filter_item); j++) {
cJSON* tag_value = cJSON_GetArrayItem(filter_item, j);
if (cJSON_IsString(tag_value)) {
tag_value_count++;
}
@@ -1266,8 +1606,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
for (int i = 0; i < tag_value_count; i++) {
if (i > 0) {
for (int j = 0; j < tag_value_count; j++) {
if (j > 0) {
snprintf(sql_ptr, remaining, ",");
sql_ptr++;
remaining--;
@@ -1282,8 +1622,8 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
// Add tag name and values to bind params
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, tag_name);
for (int i = 0; i < cJSON_GetArraySize(filter_item); i++) {
cJSON* tag_value = cJSON_GetArrayItem(filter_item, i);
for (int j = 0; j < cJSON_GetArraySize(filter_item); j++) {
cJSON* tag_value = cJSON_GetArrayItem(filter_item, j);
if (cJSON_IsString(tag_value)) {
add_bind_param(&bind_params, &bind_param_count, &bind_param_capacity, cJSON_GetStringValue(tag_value));
}
@@ -1302,12 +1642,12 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
// Escape single quotes in search term for SQL safety
char escaped_search[256];
size_t escaped_len = 0;
for (size_t i = 0; search_term[i] && escaped_len < sizeof(escaped_search) - 1; i++) {
if (search_term[i] == '\'') {
for (size_t j = 0; search_term[j] && escaped_len < sizeof(escaped_search) - 1; j++) {
if (search_term[j] == '\'') {
escaped_search[escaped_len++] = '\'';
escaped_search[escaped_len++] = '\'';
} else {
escaped_search[escaped_len++] = search_term[i];
escaped_search[escaped_len++] = search_term[j];
}
}
escaped_search[escaped_len] = '\0';
@@ -1328,7 +1668,7 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
}
// Handle until filter
cJSON* until = cJSON_GetObjectItemCaseSensitive(filter, "until");
if (until && cJSON_IsNumber(until)) {
@@ -1336,12 +1676,12 @@ int handle_req_message(const char* sub_id, cJSON* filters, struct lws *wsi, stru
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
}
// Add ordering and limit
snprintf(sql_ptr, remaining, " ORDER BY created_at DESC");
sql_ptr += strlen(sql_ptr);
remaining = sizeof(sql) - strlen(sql);
// Handle limit filter
cJSON* limit = cJSON_GetObjectItemCaseSensitive(filter, "limit");
if (limit && cJSON_IsNumber(limit)) {
@@ -1354,123 +1694,27 @@ 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 = db_prepare(sql, &stmt);
if (rc != SQLITE_OK) {
char error_msg[256];
snprintf(error_msg, sizeof(error_msg), "Failed to prepare subscription query: %s", db_last_error());
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",
db_last_error(),
sql);
fflush(stderr);
}
// Submit async REQ query (results processed on lws thread via completion queue)
if (submit_req_query_async(async_state, sql, (const char**)bind_params, bind_param_count) != 0) {
DEBUG_ERROR("Failed to submit async REQ query for subscription %s", sub_id);
continue;
}
// Track query execution for abuse detection
submitted_jobs++;
if (pss) {
pss->db_queries_executed++;
}
// Bind parameters
for (int i = 0; i < bind_param_count; i++) {
db_bind_text_param(stmt, i + 1, bind_params[i]);
}
// Cache config values outside the row loop (performance fix)
int expiration_enabled = get_config_bool("expiration_enabled", 1);
int filter_responses = get_config_bool("expiration_filter", 1);
int row_count = 0;
while (db_step_stmt(stmt) == SQLITE_ROW) {
row_count++;
// Track rows returned for abuse detection
if (pss) {
pss->db_rows_returned++;
}
// Get pre-serialized event JSON (no reconstruction needed!)
const char* event_json_str = db_column_text_value(stmt, 0);
if (!event_json_str) {
DEBUG_ERROR("Event has NULL event_json field");
continue;
}
// Parse event JSON only for expiration check
cJSON* event = cJSON_Parse(event_json_str);
if (!event) {
DEBUG_ERROR("Failed to parse event_json from database");
continue;
}
// Check expiration filtering (NIP-40) at application level
// (expiration_enabled and filter_responses are cached outside the loop)
if (expiration_enabled && filter_responses) {
time_t current_time = time(NULL);
if (is_event_expired(event, current_time)) {
// Skip this expired event
cJSON_Delete(event);
continue;
}
}
// Build EVENT message using zero-copy path: allocate with LWS_PRE prefix,
// write directly, transfer ownership to queue — no memcpy.
// Format: ["EVENT","<sub_id>",<event_json>]
size_t sub_id_len = strlen(sub_id);
size_t event_json_len = strlen(event_json_str);
size_t msg_len = 10 + sub_id_len + 3 + event_json_len + 1;
unsigned char* buf = malloc(LWS_PRE + msg_len + 1);
if (buf) {
char* msg_ptr = (char*)(buf + LWS_PRE);
snprintf(msg_ptr, msg_len + 1, "[\"EVENT\",\"%s\",%s]", sub_id, event_json_str);
size_t actual_len = strlen(msg_ptr);
// queue_message_take_ownership takes buf ownership — no memcpy, no free needed here
if (queue_message_take_ownership(wsi, pss, buf, actual_len, LWS_WRITE_TEXT) != 0) {
DEBUG_ERROR("Failed to queue EVENT message for sub=%s", sub_id);
// buf already freed by queue_message_take_ownership on failure
}
}
cJSON_Delete(event);
events_sent++;
}
db_finalize_stmt(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
free_bind_params(bind_params, bind_param_count);
return events_sent;
if (submitted_jobs == 0) {
req_async_state_free(async_state);
return events_sent;
}
return HANDLE_REQ_ASYNC_PENDING;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
@@ -1987,6 +2231,44 @@ int main(int argc, char* argv[]) {
nostr_cleanup();
return 1;
}
// Pre-warm unified config cache while DB is still available.
// This is critical because runtime strict mode sets g_db = NULL,
// and admin authorization depends on relay_pubkey/admin_pubkey reads.
if (reload_config_from_table() != 0) {
DEBUG_ERROR("Failed to pre-warm configuration cache from config table");
cleanup_configuration_system();
nostr_cleanup();
close_database();
return 1;
}
const char* prewarmed_relay_pubkey = get_config_value("relay_pubkey");
const char* prewarmed_admin_pubkey = get_config_value("admin_pubkey");
if (!prewarmed_relay_pubkey || strlen(prewarmed_relay_pubkey) != 64 ||
!prewarmed_admin_pubkey || strlen(prewarmed_admin_pubkey) != 64) {
if (prewarmed_relay_pubkey) free((char*)prewarmed_relay_pubkey);
if (prewarmed_admin_pubkey) free((char*)prewarmed_admin_pubkey);
DEBUG_ERROR("Critical config pre-warm validation failed (relay_pubkey/admin_pubkey missing)");
cleanup_configuration_system();
nostr_cleanup();
close_database();
return 1;
}
free((char*)prewarmed_relay_pubkey);
free((char*)prewarmed_admin_pubkey);
// Preload relay private key runtime cache while DB is still available.
// Required for admin command decrypt/encrypt after strict mode sets g_db = NULL.
if (preload_relay_private_key_cache() != 0) {
DEBUG_ERROR("Failed to pre-warm relay private key cache");
cleanup_configuration_system();
nostr_cleanup();
close_database();
return 1;
}
DEBUG_INFO("Configuration cache pre-warmed with relay_pubkey/admin_pubkey and relay private key");
// Configuration system is now fully initialized with event-based approach
// All configuration is loaded from database events
@@ -2052,12 +2334,12 @@ int main(int argc, char* argv[]) {
ip_ban_load_from_db();
// Optional thread pool scaffold initialization (execution wiring is future work)
int thread_pool_enabled = get_config_bool("thread_pool_enabled", 0);
int thread_pool_enabled = get_config_bool("thread_pool_enabled", 1);
int thread_pool_initialized = 0;
if (thread_pool_enabled) {
thread_pool_config_t tp_cfg;
memset(&tp_cfg, 0, sizeof(tp_cfg));
tp_cfg.reader_threads = get_config_int("thread_pool_readers", 4);
tp_cfg.reader_threads = get_config_int("thread_pool_readers", 1);
tp_cfg.max_queue_depth = get_config_int("thread_pool_max_queue_depth", 4096);
tp_cfg.db_path = g_database_path;
tp_cfg.wake_loop_cb = wake_event_loop_from_thread_pool;
@@ -2071,6 +2353,10 @@ int main(int argc, char* argv[]) {
}
}
// Phase 5 strict mode: remove main-thread fallback to global DB handle.
// Runtime DB access must go through thread-bound worker connections.
g_db = NULL;
// 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
+21 -3
View File
@@ -12,9 +12,9 @@
// Version information (auto-updated by build system)
// Using CRELAY_ prefix to avoid conflicts with nostr_core_lib VERSION macros
#define CRELAY_VERSION_MAJOR 2
#define CRELAY_VERSION_MINOR 0
#define CRELAY_VERSION_PATCH 3
#define CRELAY_VERSION "v2.0.3"
#define CRELAY_VERSION_MINOR 1
#define CRELAY_VERSION_PATCH 6
#define CRELAY_VERSION "v2.1.6"
// Relay metadata (authoritative source for NIP-11 information)
#define RELAY_NAME "C-Relay"
@@ -28,4 +28,22 @@
#define POSTING_POLICY ""
#define PAYMENTS_URL ""
// Forward declaration to avoid pulling cJSON headers into all includers.
typedef struct cJSON cJSON;
// Async REQ handler status used by websocket callback to defer EOSE until worker completion
#define HANDLE_REQ_ASYNC_PENDING (-2)
// Async EVENT storage split:
// - store_event_core(): worker-safe core DB write path
// returns 0 when inserted, 1 when handled without insert (duplicate/ephemeral), -1 on error
// - store_event_post_actions(): main-thread-only follow-up actions (monitoring/tags/WoT sync)
// - store_event(): legacy wrapper preserving original behavior for synchronous call sites
int store_event_core(cJSON* event);
void store_event_post_actions(cJSON* event);
int store_event(cJSON* event);
// Drains completed async REQ jobs and queues EVENT/EOSE on the lws service thread.
void process_req_async_completions(void);
#endif /* MAIN_H */
+71 -36
View File
@@ -8,7 +8,7 @@
#include <libwebsockets.h>
#include "../nostr_core_lib/cjson/cJSON.h"
#include "config.h"
#include "main.h"
// Forward declarations for configuration functions
const char* get_config_value(const char* key);
@@ -23,6 +23,67 @@ int get_config_bool(const char* key, int default_value);
#define HTTP_STATUS_INTERNAL_SERVER_ERROR 500
#define NIP11_CACHE_TTL_SEC 5
static pthread_mutex_t g_nip11_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
static char* g_nip11_cached_json = NULL;
static size_t g_nip11_cached_json_len = 0;
static time_t g_nip11_cache_expires_at = 0;
cJSON* generate_relay_info_json();
static void invalidate_nip11_cache_locked(void) {
if (g_nip11_cached_json) {
free(g_nip11_cached_json);
g_nip11_cached_json = NULL;
}
g_nip11_cached_json_len = 0;
g_nip11_cache_expires_at = 0;
}
static char* get_nip11_json_cached_dup(size_t* out_len) {
time_t now = time(NULL);
pthread_mutex_lock(&g_nip11_cache_mutex);
if (g_nip11_cached_json && now < g_nip11_cache_expires_at) {
char* copy = strdup(g_nip11_cached_json);
if (copy && out_len) {
*out_len = g_nip11_cached_json_len;
}
pthread_mutex_unlock(&g_nip11_cache_mutex);
return copy;
}
pthread_mutex_unlock(&g_nip11_cache_mutex);
cJSON* info_json = generate_relay_info_json();
if (!info_json) {
return NULL;
}
char* fresh_json = cJSON_Print(info_json);
cJSON_Delete(info_json);
if (!fresh_json) {
return NULL;
}
size_t fresh_len = strlen(fresh_json);
pthread_mutex_lock(&g_nip11_cache_mutex);
invalidate_nip11_cache_locked();
g_nip11_cached_json = strdup(fresh_json);
if (g_nip11_cached_json) {
g_nip11_cached_json_len = fresh_len;
g_nip11_cache_expires_at = now + NIP11_CACHE_TTL_SEC;
}
pthread_mutex_unlock(&g_nip11_cache_mutex);
if (out_len) {
*out_len = fresh_len;
}
return fresh_json;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// NIP-11 RELAY INFORMATION DOCUMENT
@@ -80,8 +141,9 @@ void init_relay_info() {
// Clean up relay information JSON objects
void cleanup_relay_info() {
// NIP-11 relay information is now generated dynamically from config table
// No cleanup needed - data is fetched directly from database when requested
pthread_mutex_lock(&g_nip11_cache_mutex);
invalidate_nip11_cache_locked();
pthread_mutex_unlock(&g_nip11_cache_mutex);
}
// Generate NIP-11 compliant JSON document
@@ -184,14 +246,14 @@ cJSON* generate_relay_info_json() {
cJSON_AddStringToObject(info, "software", relay_software);
free((char*)relay_software);
} else {
cJSON_AddStringToObject(info, "software", "https://git.laantungir.net/laantungir/c-relay.git");
cJSON_AddStringToObject(info, "software", RELAY_SOFTWARE);
}
if (relay_version && strlen(relay_version) > 0) {
cJSON_AddStringToObject(info, "version", relay_version);
free((char*)relay_version);
} else {
cJSON_AddStringToObject(info, "version", "0.2.0");
cJSON_AddStringToObject(info, "version", RELAY_VERSION);
}
// Add policies
@@ -348,9 +410,10 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
return -1; // Close connection
}
// Generate relay information JSON
cJSON* info_json = generate_relay_info_json();
if (!info_json) {
// Generate (or fetch cached) relay information JSON
size_t json_len = 0;
char* json_string = get_nip11_json_cached_dup(&json_len);
if (!json_string) {
DEBUG_ERROR("Failed to generate relay info JSON");
unsigned char buf[LWS_PRE + 256];
unsigned char *p = &buf[LWS_PRE];
@@ -373,34 +436,6 @@ int handle_nip11_http_request(struct lws* wsi, const char* accept_header) {
return -1;
}
char* json_string = cJSON_Print(info_json);
cJSON_Delete(info_json);
if (!json_string) {
DEBUG_ERROR("Failed to serialize relay info JSON");
unsigned char buf[LWS_PRE + 256];
unsigned char *p = &buf[LWS_PRE];
unsigned char *start = p;
unsigned char *end = &buf[sizeof(buf) - 1];
if (lws_add_http_header_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, &p, end)) {
return -1;
}
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char*)"text/plain", 10, &p, end)) {
return -1;
}
if (lws_add_http_header_content_length(wsi, 0, &p, end)) {
return -1;
}
if (lws_finalize_http_header(wsi, &p, end)) {
return -1;
}
lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
return -1;
}
size_t json_len = strlen(json_string);
// Allocate session data to manage buffer lifetime across callbacks
struct nip11_session_data* session_data = malloc(sizeof(struct nip11_session_data));
if (!session_data) {
+22 -3
View File
@@ -129,6 +129,14 @@ typedef struct {
static nip42_challenge_manager_t g_challenge_manager = {0};
static int g_validator_initialized = 0;
typedef struct {
int has_active_whitelist_rules;
time_t last_refresh;
} auth_rules_fast_cache_t;
static auth_rules_fast_cache_t g_auth_rules_fast_cache = {0};
#define AUTH_RULES_CACHE_TTL_SEC 5
// Last rule violation details for status code mapping
struct {
char violation_type[100]; // "pubkey_blacklist", "hash_blacklist",
@@ -502,10 +510,10 @@ void nostr_request_result_free_file_data(nostr_request_result_t *result) {
/**
* Force cache refresh - cache no longer exists, function kept for compatibility
* Force cache refresh for auth rule fast cache.
*/
void nostr_request_validator_force_cache_refresh(void) {
// Cache no longer exists - direct database queries are used
g_auth_rules_fast_cache.last_refresh = 0;
}
/**
@@ -520,6 +528,17 @@ static int reload_auth_config(void) {
// Note: Blossom protocol validation removed - C-relay uses standard Nostr events only
static int has_active_whitelist_rules_cached(void) {
time_t now = time(NULL);
if (g_auth_rules_fast_cache.last_refresh == 0 ||
(now - g_auth_rules_fast_cache.last_refresh) >= AUTH_RULES_CACHE_TTL_SEC) {
g_auth_rules_fast_cache.has_active_whitelist_rules =
(db_count_active_whitelist_rules() > 0) ? 1 : 0;
g_auth_rules_fast_cache.last_refresh = now;
}
return g_auth_rules_fast_cache.has_active_whitelist_rules;
}
/**
* Check database authentication rules for the request
* Implements the 6-step rule evaluation engine from AUTH_API.md
@@ -553,7 +572,7 @@ int check_database_auth_rules(const char *pubkey, const char *operation __attrib
}
// Step 4: If any whitelist rules exist, deny by default
if (db_count_active_whitelist_rules() > 0) {
if (has_active_whitelist_rules_cached()) {
strcpy(g_last_rule_violation.violation_type, "whitelist_violation");
strcpy(g_last_rule_violation.reason,
"Public key not whitelisted for this operation");
+146 -4
View File
@@ -2,6 +2,7 @@
#include <cjson/cJSON.h>
#include "debug.h"
#include "db_ops.h"
#include "thread_pool.h"
#include <string.h>
#include <stdlib.h>
#include <time.h>
@@ -10,6 +11,34 @@
#include <libwebsockets.h>
#include "subscriptions.h"
static void free_sub_log_created_payload_local(thread_pool_sub_log_created_payload_t* payload) {
if (!payload) return;
free(payload->sub_id);
free(payload->wsi_ptr);
free(payload->client_ip);
free(payload->filter_json);
free(payload);
}
static void free_sub_log_closed_payload_local(thread_pool_sub_log_closed_payload_t* payload) {
if (!payload) return;
free(payload->sub_id);
free(payload->client_ip);
free(payload);
}
static void free_sub_log_disconnected_payload_local(thread_pool_sub_log_disconnected_payload_t* payload) {
if (!payload) return;
free(payload->client_ip);
free(payload);
}
static void free_sub_update_events_payload_local(thread_pool_sub_update_events_payload_t* payload) {
if (!payload) return;
free(payload->sub_id);
free(payload);
}
// Forward declarations for logging functions
// Forward declarations for configuration functions
@@ -1040,7 +1069,43 @@ void log_subscription_created(const subscription_t* sub) {
cJSON_Delete(filters_array);
}
db_log_subscription_created(sub->id, wsi_str, sub->client_ip, filter_json ? filter_json : "[]");
if (!thread_pool_is_running()) {
db_log_subscription_created(sub->id, wsi_str, sub->client_ip, filter_json ? filter_json : "[]");
if (filter_json) free(filter_json);
return;
}
thread_pool_sub_log_created_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) {
if (filter_json) free(filter_json);
return;
}
payload->sub_id = strdup(sub->id);
payload->wsi_ptr = strdup(wsi_str);
payload->client_ip = strdup(sub->client_ip);
payload->filter_json = strdup(filter_json ? filter_json : "[]");
if (!payload->sub_id || !payload->wsi_ptr || !payload->client_ip || !payload->filter_json) {
free(payload->sub_id);
free(payload->wsi_ptr);
free(payload->client_ip);
free(payload->filter_json);
free(payload);
if (filter_json) free(filter_json);
return;
}
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_LOG_SUB_CREATED;
job.payload = payload;
job.payload_free = (thread_pool_payload_free_cb)free_sub_log_created_payload_local;
thread_pool_status_t submit_rc = thread_pool_submit_write(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_sub_log_created_payload_local(payload);
}
if (filter_json) free(filter_json);
}
@@ -1050,14 +1115,65 @@ void log_subscription_closed(const char* sub_id, const char* client_ip, const ch
(void)reason; // Mark as intentionally unused
if (!sub_id) return;
db_log_subscription_closed(sub_id, client_ip);
if (!thread_pool_is_running()) {
db_log_subscription_closed(sub_id, client_ip);
return;
}
thread_pool_sub_log_closed_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) {
return;
}
payload->sub_id = strdup(sub_id);
payload->client_ip = strdup(client_ip ? client_ip : "unknown");
if (!payload->sub_id || !payload->client_ip) {
free_sub_log_closed_payload_local(payload);
return;
}
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_LOG_SUB_CLOSED;
job.payload = payload;
job.payload_free = (thread_pool_payload_free_cb)free_sub_log_closed_payload_local;
thread_pool_status_t submit_rc = thread_pool_submit_write(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_sub_log_closed_payload_local(payload);
}
}
// Log subscription disconnection to database
void log_subscription_disconnected(const char* client_ip) {
if (!client_ip) return;
db_log_subscription_disconnected(client_ip);
if (!thread_pool_is_running()) {
db_log_subscription_disconnected(client_ip);
return;
}
thread_pool_sub_log_disconnected_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) {
return;
}
payload->client_ip = strdup(client_ip);
if (!payload->client_ip) {
free_sub_log_disconnected_payload_local(payload);
return;
}
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_LOG_SUB_DISCONNECTED;
job.payload = payload;
job.payload_free = (thread_pool_payload_free_cb)free_sub_log_disconnected_payload_local;
thread_pool_status_t submit_rc = thread_pool_submit_write(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_sub_log_disconnected_payload_local(payload);
}
}
// Log event broadcast to database (optional, can be resource intensive)
@@ -1085,7 +1201,33 @@ void log_subscription_disconnected(const char* client_ip) {
void update_subscription_events_sent(const char* sub_id, int events_sent) {
if (!sub_id) return;
db_update_subscription_events_sent(sub_id, events_sent);
if (!thread_pool_is_running()) {
db_update_subscription_events_sent(sub_id, events_sent);
return;
}
thread_pool_sub_update_events_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) {
return;
}
payload->sub_id = strdup(sub_id);
payload->events_sent = events_sent;
if (!payload->sub_id) {
free_sub_update_events_payload_local(payload);
return;
}
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_UPDATE_SUB_EVENTS_SENT;
job.payload = payload;
job.payload_free = (thread_pool_payload_free_cb)free_sub_update_events_payload_local;
thread_pool_status_t submit_rc = thread_pool_submit_write(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_sub_update_events_payload_local(payload);
}
}
// Cleanup all subscriptions on startup
+674 -9
View File
@@ -2,8 +2,13 @@
#include "thread_pool.h"
#include "debug.h"
#include "db_ops.h"
#include "ip_ban.h"
#include <sqlite3.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -36,11 +41,93 @@ typedef struct {
thread_pool_wake_loop_cb wake_loop_cb;
void* wake_loop_ctx;
char db_path[512];
pthread_mutex_t state_mutex;
} thread_pool_state_t;
static thread_pool_state_t g_pool = {0};
typedef struct {
pthread_mutex_t mutex;
pthread_cond_t cond;
int done;
thread_pool_status_t status;
void* result_data;
size_t result_size;
} thread_pool_wait_ctx_t;
static void wait_ctx_init(thread_pool_wait_ctx_t* ctx) {
memset(ctx, 0, sizeof(*ctx));
pthread_mutex_init(&ctx->mutex, NULL);
pthread_cond_init(&ctx->cond, NULL);
}
static void wait_ctx_destroy(thread_pool_wait_ctx_t* ctx) {
pthread_mutex_destroy(&ctx->mutex);
pthread_cond_destroy(&ctx->cond);
}
static void wait_ctx_result_cb(const thread_pool_result_t* result, void* user_ctx) {
thread_pool_wait_ctx_t* ctx = (thread_pool_wait_ctx_t*)user_ctx;
if (!ctx || !result) return;
pthread_mutex_lock(&ctx->mutex);
ctx->status = result->status;
ctx->result_data = result->result_data;
ctx->result_size = result->result_size;
ctx->done = 1;
pthread_cond_signal(&ctx->cond);
pthread_mutex_unlock(&ctx->mutex);
}
static void wait_ctx_wait(thread_pool_wait_ctx_t* ctx) {
pthread_mutex_lock(&ctx->mutex);
while (!ctx->done) {
pthread_cond_wait(&ctx->cond, &ctx->mutex);
}
pthread_mutex_unlock(&ctx->mutex);
}
static void free_count_payload(void* p) {
thread_pool_count_payload_t* payload = (thread_pool_count_payload_t*)p;
if (!payload) return;
free(payload->sql);
if (payload->bind_params) {
for (int i = 0; i < payload->bind_param_count; i++) {
free(payload->bind_params[i]);
}
free(payload->bind_params);
}
free(payload);
}
static void free_req_payload(void* p) {
thread_pool_req_payload_t* payload = (thread_pool_req_payload_t*)p;
if (!payload) return;
free(payload->sql);
if (payload->bind_params) {
for (int i = 0; i < payload->bind_param_count; i++) {
free(payload->bind_params[i]);
}
free(payload->bind_params);
}
free(payload);
}
static void free_store_event_payload(void* p) {
thread_pool_store_event_payload_t* payload = (thread_pool_store_event_payload_t*)p;
if (!payload) return;
free(payload->id);
free(payload->pubkey);
free(payload->event_type);
free(payload->content);
free(payload->sig);
free(payload->tags_json);
free(payload->event_json);
free(payload);
}
static void queue_init(thread_pool_queue_t* q, int max_size) {
memset(q, 0, sizeof(*q));
q->max_size = (max_size > 0) ? max_size : 4096;
@@ -112,13 +199,39 @@ static void wake_event_loop(void) {
}
}
static void complete_job_with_status(thread_pool_job_node_t* node, thread_pool_status_t status, const char* message) {
static sqlite3* open_worker_connection(void) {
if (g_pool.db_path[0] == '\0') {
return NULL;
}
sqlite3* db = NULL;
int rc = sqlite3_open_v2(g_pool.db_path, &db, SQLITE_OPEN_READWRITE, NULL);
if (rc != SQLITE_OK) {
if (db) {
sqlite3_close(db);
}
return NULL;
}
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", NULL, NULL, NULL);
sqlite3_busy_timeout(db, 5000);
return db;
}
static void complete_job_with_result(thread_pool_job_node_t* node,
thread_pool_status_t status,
const char* message,
void* result_data,
size_t result_size) {
thread_pool_result_t result;
memset(&result, 0, sizeof(result));
result.job_id = node->job_id;
result.type = node->job.type;
result.status = status;
result.session = node->job.session;
result.result_data = result_data;
result.result_size = result_size;
result.message = message;
if (node->job.result_cb) {
@@ -132,29 +245,305 @@ static void complete_job_with_status(thread_pool_job_node_t* node, thread_pool_s
free(node);
}
static void execute_count_job(thread_pool_job_node_t* node) {
thread_pool_count_payload_t* payload = (thread_pool_count_payload_t*)node->job.payload;
if (!payload || !payload->sql) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"COUNT payload missing", NULL, 0);
return;
}
int count = 0;
if (db_count_with_sql(payload->sql, (const char**)payload->bind_params,
payload->bind_param_count, &count) != 0) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"COUNT query failed", NULL, 0);
return;
}
thread_pool_count_result_t* out = calloc(1, sizeof(*out));
if (!out) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"COUNT result allocation failed", NULL, 0);
return;
}
out->count = count;
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"COUNT query completed", out, sizeof(*out));
}
static void execute_req_job(thread_pool_job_node_t* node) {
thread_pool_req_payload_t* payload = (thread_pool_req_payload_t*)node->job.payload;
if (!payload || !payload->sql) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"REQ payload missing", NULL, 0);
return;
}
db_stmt_t* stmt = NULL;
if (db_prepare(payload->sql, &stmt) != DB_OK) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"REQ prepare failed", NULL, 0);
return;
}
for (int i = 0; i < payload->bind_param_count; i++) {
const char* v = (payload->bind_params && payload->bind_params[i]) ? payload->bind_params[i] : "";
(void)db_bind_text_param(stmt, i + 1, v);
}
int cap = (payload->row_limit > 0) ? payload->row_limit : 500;
if (cap < 1) cap = 1;
thread_pool_req_result_t* out = calloc(1, sizeof(*out));
if (!out) {
db_finalize_stmt(stmt);
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"REQ result allocation failed", NULL, 0);
return;
}
out->event_json_rows = calloc((size_t)cap, sizeof(char*));
if (!out->event_json_rows) {
db_finalize_stmt(stmt);
free(out);
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"REQ row array allocation failed", NULL, 0);
return;
}
int rc = DB_OK;
while ((rc = db_step_stmt(stmt)) == DB_ROW) {
if (out->row_count >= cap) break;
const char* row = db_column_text_value(stmt, 0);
if (!row) continue;
out->event_json_rows[out->row_count] = strdup(row);
if (!out->event_json_rows[out->row_count]) {
break;
}
out->row_count++;
}
db_finalize_stmt(stmt);
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"REQ query completed", out, sizeof(*out));
}
static void execute_store_event_job(thread_pool_job_node_t* node) {
thread_pool_store_event_payload_t* payload = (thread_pool_store_event_payload_t*)node->job.payload;
if (!payload || !payload->id || !payload->pubkey || !payload->content || !payload->sig) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"STORE_EVENT payload missing required fields", NULL, 0);
return;
}
thread_pool_store_event_result_t* out = calloc(1, sizeof(*out));
if (!out) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"STORE_EVENT result allocation failed", NULL, 0);
return;
}
int step_rc = DB_ERROR;
int extended_errcode = 0;
if (db_insert_event_with_json(payload->id,
payload->pubkey,
payload->created_at,
payload->kind,
payload->event_type ? payload->event_type : "regular",
payload->content,
payload->sig,
payload->tags_json ? payload->tags_json : "[]",
payload->event_json ? payload->event_json : "{}",
&step_rc,
&extended_errcode) != 0) {
free(out);
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"STORE_EVENT execution failed", NULL, 0);
return;
}
out->step_rc = step_rc;
out->extended_errcode = extended_errcode;
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"STORE_EVENT completed", out, sizeof(*out));
}
static void execute_sub_log_created_job(thread_pool_job_node_t* node) {
thread_pool_sub_log_created_payload_t* payload = (thread_pool_sub_log_created_payload_t*)node->job.payload;
if (!payload || !payload->sub_id || !payload->wsi_ptr || !payload->client_ip) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_LOG_CREATED payload missing", NULL, 0);
return;
}
if (db_log_subscription_created(payload->sub_id,
payload->wsi_ptr,
payload->client_ip,
payload->filter_json ? payload->filter_json : "[]") != 0) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_LOG_CREATED failed", NULL, 0);
return;
}
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"SUB_LOG_CREATED completed", NULL, 0);
}
static void execute_sub_log_closed_job(thread_pool_job_node_t* node) {
thread_pool_sub_log_closed_payload_t* payload = (thread_pool_sub_log_closed_payload_t*)node->job.payload;
if (!payload || !payload->sub_id) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_LOG_CLOSED payload missing", NULL, 0);
return;
}
if (db_log_subscription_closed(payload->sub_id, payload->client_ip) != 0) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_LOG_CLOSED failed", NULL, 0);
return;
}
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"SUB_LOG_CLOSED completed", NULL, 0);
}
static void execute_sub_log_disconnected_job(thread_pool_job_node_t* node) {
thread_pool_sub_log_disconnected_payload_t* payload = (thread_pool_sub_log_disconnected_payload_t*)node->job.payload;
if (!payload || !payload->client_ip) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_LOG_DISCONNECTED payload missing", NULL, 0);
return;
}
if (db_log_subscription_disconnected(payload->client_ip) < 0) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_LOG_DISCONNECTED failed", NULL, 0);
return;
}
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"SUB_LOG_DISCONNECTED completed", NULL, 0);
}
static void execute_sub_update_events_job(thread_pool_job_node_t* node) {
thread_pool_sub_update_events_payload_t* payload = (thread_pool_sub_update_events_payload_t*)node->job.payload;
if (!payload || !payload->sub_id) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_UPDATE_EVENTS payload missing", NULL, 0);
return;
}
if (db_update_subscription_events_sent(payload->sub_id, payload->events_sent) != 0) {
complete_job_with_result(node, THREAD_POOL_STATUS_INTERNAL_ERROR,
"SUB_UPDATE_EVENTS failed", NULL, 0);
return;
}
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"SUB_UPDATE_EVENTS completed", NULL, 0);
}
static void execute_ip_ban_save_job(thread_pool_job_node_t* node) {
ip_ban_save_to_db();
complete_job_with_result(node, THREAD_POOL_STATUS_OK,
"IP_BAN_SAVE completed", NULL, 0);
}
static void execute_read_job(thread_pool_job_node_t* node) {
switch (node->job.type) {
case THREAD_POOL_JOB_REQ_QUERY:
execute_req_job(node);
break;
case THREAD_POOL_JOB_COUNT_QUERY:
execute_count_job(node);
break;
default:
complete_job_with_result(node, THREAD_POOL_STATUS_NOT_IMPLEMENTED,
"Read job type not implemented", NULL, 0);
break;
}
}
static void execute_write_job(thread_pool_job_node_t* node) {
switch (node->job.type) {
case THREAD_POOL_JOB_STORE_EVENT:
execute_store_event_job(node);
break;
case THREAD_POOL_JOB_LOG_SUB_CREATED:
execute_sub_log_created_job(node);
break;
case THREAD_POOL_JOB_LOG_SUB_CLOSED:
execute_sub_log_closed_job(node);
break;
case THREAD_POOL_JOB_LOG_SUB_DISCONNECTED:
execute_sub_log_disconnected_job(node);
break;
case THREAD_POOL_JOB_UPDATE_SUB_EVENTS_SENT:
execute_sub_update_events_job(node);
break;
case THREAD_POOL_JOB_IP_BAN_SAVE:
execute_ip_ban_save_job(node);
break;
default:
complete_job_with_result(node, THREAD_POOL_STATUS_NOT_IMPLEMENTED,
"Write job type not implemented", NULL, 0);
break;
}
}
static void* reader_worker_main(void* arg) {
(void)arg;
int reader_index = (int)(intptr_t)arg;
char thread_name[16];
snprintf(thread_name, sizeof(thread_name), "db-read-%d", reader_index);
pthread_setname_np(pthread_self(), thread_name);
sqlite3* worker_db = open_worker_connection();
if (!worker_db) {
DEBUG_ERROR("Reader worker failed to open SQLite connection");
return NULL;
}
db_set_thread_connection(worker_db);
while (g_pool.running) {
thread_pool_job_node_t* node = queue_pop(&g_pool.read_q);
if (!node) break;
// Scaffold only: execution wiring is intentionally deferred.
complete_job_with_status(node, THREAD_POOL_STATUS_NOT_IMPLEMENTED,
"Read worker scaffold active; execution not wired yet");
execute_read_job(node);
}
db_clear_thread_connection();
sqlite3_close(worker_db);
return NULL;
}
static void* writer_worker_main(void* arg) {
(void)arg;
pthread_setname_np(pthread_self(), "db-write");
sqlite3* worker_db = open_worker_connection();
if (!worker_db) {
DEBUG_ERROR("Writer worker failed to open SQLite connection");
return NULL;
}
db_set_thread_connection(worker_db);
while (g_pool.running) {
thread_pool_job_node_t* node = queue_pop(&g_pool.write_q);
if (!node) break;
// Scaffold only: execution wiring is intentionally deferred.
complete_job_with_status(node, THREAD_POOL_STATUS_NOT_IMPLEMENTED,
"Write worker scaffold active; execution not wired yet");
execute_write_job(node);
}
db_clear_thread_connection();
sqlite3_close(worker_db);
return NULL;
}
@@ -180,10 +569,21 @@ int thread_pool_init(const thread_pool_config_t* config) {
g_pool.next_job_id = 1;
g_pool.wake_loop_cb = config->wake_loop_cb;
g_pool.wake_loop_ctx = config->wake_loop_ctx;
const char* db_path = (config->db_path && config->db_path[0] != '\0') ? config->db_path : db_get_database_path();
if (!db_path || db_path[0] == '\0') {
free(g_pool.readers);
g_pool.readers = NULL;
pthread_mutex_unlock(&g_pool.state_mutex);
return -1;
}
strncpy(g_pool.db_path, db_path, sizeof(g_pool.db_path) - 1);
g_pool.db_path[sizeof(g_pool.db_path) - 1] = '\0';
g_pool.running = 1;
for (int i = 0; i < g_pool.reader_count; i++) {
if (pthread_create(&g_pool.readers[i], NULL, reader_worker_main, NULL) != 0) {
if (pthread_create(&g_pool.readers[i], NULL, reader_worker_main, (void*)(intptr_t)(i + 1)) != 0) {
g_pool.running = 0;
pthread_cond_broadcast(&g_pool.read_q.cond);
pthread_cond_broadcast(&g_pool.write_q.cond);
@@ -270,6 +670,271 @@ thread_pool_status_t thread_pool_submit_write(const thread_pool_job_t* job, uint
return submit_to_queue(&g_pool.write_q, job, out_job_id);
}
int thread_pool_execute_count_sync(const char* sql, const char** bind_params, int bind_param_count, int* out_count) {
if (!sql || !out_count) {
return -1;
}
if (!thread_pool_is_running()) {
return db_count_with_sql(sql, bind_params, bind_param_count, out_count);
}
thread_pool_count_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) return -1;
payload->sql = strdup(sql);
payload->bind_param_count = bind_param_count;
if (!payload->sql) {
free_count_payload(payload);
return -1;
}
if (bind_param_count > 0) {
payload->bind_params = calloc((size_t)bind_param_count, sizeof(char*));
if (!payload->bind_params) {
free_count_payload(payload);
return -1;
}
for (int i = 0; i < bind_param_count; i++) {
const char* v = (bind_params && bind_params[i]) ? bind_params[i] : "";
payload->bind_params[i] = strdup(v);
if (!payload->bind_params[i]) {
free_count_payload(payload);
return -1;
}
}
}
thread_pool_wait_ctx_t wait_ctx;
wait_ctx_init(&wait_ctx);
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_COUNT_QUERY;
job.payload = payload;
job.payload_free = free_count_payload;
job.result_cb = wait_ctx_result_cb;
job.result_cb_ctx = &wait_ctx;
thread_pool_status_t submit_rc = thread_pool_submit_read(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_count_payload(payload);
wait_ctx_destroy(&wait_ctx);
return -1;
}
wait_ctx_wait(&wait_ctx);
int rc = -1;
if (wait_ctx.status == THREAD_POOL_STATUS_OK && wait_ctx.result_data) {
thread_pool_count_result_t* result = (thread_pool_count_result_t*)wait_ctx.result_data;
*out_count = result->count;
free(result);
rc = 0;
}
wait_ctx_destroy(&wait_ctx);
return rc;
}
int thread_pool_execute_req_sync(const char* sql, const char** bind_params, int bind_param_count,
int row_limit, thread_pool_req_result_t** out_result) {
if (!sql || !out_result) {
return -1;
}
if (!thread_pool_is_running()) {
db_stmt_t* stmt = NULL;
if (db_prepare(sql, &stmt) != DB_OK) {
return -1;
}
for (int i = 0; i < bind_param_count; i++) {
const char* v = (bind_params && bind_params[i]) ? bind_params[i] : "";
(void)db_bind_text_param(stmt, i + 1, v);
}
int cap = (row_limit > 0) ? row_limit : 500;
if (cap < 1) cap = 1;
thread_pool_req_result_t* result = calloc(1, sizeof(*result));
if (!result) {
db_finalize_stmt(stmt);
return -1;
}
result->event_json_rows = calloc((size_t)cap, sizeof(char*));
if (!result->event_json_rows) {
free(result);
db_finalize_stmt(stmt);
return -1;
}
int step_rc = DB_OK;
while ((step_rc = db_step_stmt(stmt)) == DB_ROW) {
if (result->row_count >= cap) break;
const char* row = db_column_text_value(stmt, 0);
if (!row) continue;
result->event_json_rows[result->row_count] = strdup(row);
if (!result->event_json_rows[result->row_count]) {
break;
}
result->row_count++;
}
db_finalize_stmt(stmt);
*out_result = result;
return 0;
}
thread_pool_req_payload_t* payload = calloc(1, sizeof(*payload));
if (!payload) return -1;
payload->sql = strdup(sql);
payload->bind_param_count = bind_param_count;
payload->row_limit = row_limit;
if (!payload->sql) {
free_req_payload(payload);
return -1;
}
if (bind_param_count > 0) {
payload->bind_params = calloc((size_t)bind_param_count, sizeof(char*));
if (!payload->bind_params) {
free_req_payload(payload);
return -1;
}
for (int i = 0; i < bind_param_count; i++) {
const char* v = (bind_params && bind_params[i]) ? bind_params[i] : "";
payload->bind_params[i] = strdup(v);
if (!payload->bind_params[i]) {
free_req_payload(payload);
return -1;
}
}
}
thread_pool_wait_ctx_t wait_ctx;
wait_ctx_init(&wait_ctx);
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_REQ_QUERY;
job.payload = payload;
job.payload_free = free_req_payload;
job.result_cb = wait_ctx_result_cb;
job.result_cb_ctx = &wait_ctx;
thread_pool_status_t submit_rc = thread_pool_submit_read(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_req_payload(payload);
wait_ctx_destroy(&wait_ctx);
return -1;
}
wait_ctx_wait(&wait_ctx);
int rc = -1;
if (wait_ctx.status == THREAD_POOL_STATUS_OK && wait_ctx.result_data) {
*out_result = (thread_pool_req_result_t*)wait_ctx.result_data;
rc = 0;
}
wait_ctx_destroy(&wait_ctx);
return rc;
}
void thread_pool_free_req_result(thread_pool_req_result_t* result) {
if (!result) return;
if (result->event_json_rows) {
for (int i = 0; i < result->row_count; i++) {
free(result->event_json_rows[i]);
}
free(result->event_json_rows);
}
free(result);
}
int thread_pool_execute_store_event_sync(const thread_pool_store_event_payload_t* payload,
thread_pool_store_event_result_t* out_result) {
if (!payload || !out_result || !payload->id || !payload->pubkey || !payload->content || !payload->sig) {
return -1;
}
if (!thread_pool_is_running()) {
int step_rc = DB_ERROR;
int extended_errcode = 0;
if (db_insert_event_with_json(payload->id,
payload->pubkey,
payload->created_at,
payload->kind,
payload->event_type ? payload->event_type : "regular",
payload->content,
payload->sig,
payload->tags_json ? payload->tags_json : "[]",
payload->event_json ? payload->event_json : "{}",
&step_rc,
&extended_errcode) != 0) {
return -1;
}
out_result->step_rc = step_rc;
out_result->extended_errcode = extended_errcode;
return 0;
}
thread_pool_store_event_payload_t* payload_copy = calloc(1, sizeof(*payload_copy));
if (!payload_copy) return -1;
payload_copy->id = payload->id ? strdup(payload->id) : NULL;
payload_copy->pubkey = payload->pubkey ? strdup(payload->pubkey) : NULL;
payload_copy->created_at = payload->created_at;
payload_copy->kind = payload->kind;
payload_copy->event_type = payload->event_type ? strdup(payload->event_type) : strdup("regular");
payload_copy->content = payload->content ? strdup(payload->content) : NULL;
payload_copy->sig = payload->sig ? strdup(payload->sig) : NULL;
payload_copy->tags_json = payload->tags_json ? strdup(payload->tags_json) : strdup("[]");
payload_copy->event_json = payload->event_json ? strdup(payload->event_json) : strdup("{}");
if (!payload_copy->id || !payload_copy->pubkey || !payload_copy->event_type ||
!payload_copy->content || !payload_copy->sig || !payload_copy->tags_json || !payload_copy->event_json) {
free_store_event_payload(payload_copy);
return -1;
}
thread_pool_wait_ctx_t wait_ctx;
wait_ctx_init(&wait_ctx);
thread_pool_job_t job;
memset(&job, 0, sizeof(job));
job.type = THREAD_POOL_JOB_STORE_EVENT;
job.payload = payload_copy;
job.payload_free = free_store_event_payload;
job.result_cb = wait_ctx_result_cb;
job.result_cb_ctx = &wait_ctx;
thread_pool_status_t submit_rc = thread_pool_submit_write(&job, NULL);
if (submit_rc != THREAD_POOL_STATUS_OK) {
free_store_event_payload(payload_copy);
wait_ctx_destroy(&wait_ctx);
return -1;
}
wait_ctx_wait(&wait_ctx);
int rc = -1;
if (wait_ctx.status == THREAD_POOL_STATUS_OK && wait_ctx.result_data) {
thread_pool_store_event_result_t* result = (thread_pool_store_event_result_t*)wait_ctx.result_data;
out_result->step_rc = result->step_rc;
out_result->extended_errcode = result->extended_errcode;
free(result);
rc = 0;
}
wait_ctx_destroy(&wait_ctx);
return rc;
}
__attribute__((constructor))
static void thread_pool_state_init_once(void) {
pthread_mutex_init(&g_pool.state_mutex, NULL);
+72
View File
@@ -13,6 +13,11 @@ typedef enum {
THREAD_POOL_JOB_COUNT_QUERY,
THREAD_POOL_JOB_STORE_EVENT,
THREAD_POOL_JOB_DELETE_EVENT,
THREAD_POOL_JOB_LOG_SUB_CREATED,
THREAD_POOL_JOB_LOG_SUB_CLOSED,
THREAD_POOL_JOB_LOG_SUB_DISCONNECTED,
THREAD_POOL_JOB_UPDATE_SUB_EVENTS_SENT,
THREAD_POOL_JOB_IP_BAN_SAVE,
THREAD_POOL_JOB_CUSTOM
} thread_pool_job_type_t;
@@ -56,6 +61,66 @@ typedef struct {
void* wake_loop_ctx;
} thread_pool_config_t;
typedef struct {
char* sql;
char** bind_params;
int bind_param_count;
} thread_pool_count_payload_t;
typedef struct {
char* sql;
char** bind_params;
int bind_param_count;
int row_limit;
} thread_pool_req_payload_t;
typedef struct {
char* id;
char* pubkey;
long long created_at;
int kind;
char* event_type;
char* content;
char* sig;
char* tags_json;
char* event_json;
} thread_pool_store_event_payload_t;
typedef struct {
int count;
} thread_pool_count_result_t;
typedef struct {
char** event_json_rows;
int row_count;
} thread_pool_req_result_t;
typedef struct {
int step_rc;
int extended_errcode;
} thread_pool_store_event_result_t;
typedef struct {
char* sub_id;
char* wsi_ptr;
char* client_ip;
char* filter_json;
} thread_pool_sub_log_created_payload_t;
typedef struct {
char* sub_id;
char* client_ip;
} thread_pool_sub_log_closed_payload_t;
typedef struct {
char* client_ip;
} thread_pool_sub_log_disconnected_payload_t;
typedef struct {
char* sub_id;
int events_sent;
} thread_pool_sub_update_events_payload_t;
int thread_pool_init(const thread_pool_config_t* config);
void thread_pool_shutdown(void);
int thread_pool_is_running(void);
@@ -63,6 +128,13 @@ int thread_pool_is_running(void);
thread_pool_status_t thread_pool_submit_read(const thread_pool_job_t* job, uint64_t* out_job_id);
thread_pool_status_t thread_pool_submit_write(const thread_pool_job_t* job, uint64_t* out_job_id);
int thread_pool_execute_count_sync(const char* sql, const char** bind_params, int bind_param_count, int* out_count);
int thread_pool_execute_req_sync(const char* sql, const char** bind_params, int bind_param_count,
int row_limit, thread_pool_req_result_t** out_result);
void thread_pool_free_req_result(thread_pool_req_result_t* result);
int thread_pool_execute_store_event_sync(const thread_pool_store_event_payload_t* payload,
thread_pool_store_event_result_t* out_result);
#ifdef __cplusplus
}
#endif
+923 -154
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -99,6 +99,10 @@ struct per_session_data {
// Get current active WebSocket connection count
int get_active_connection_count(void);
// Safely resolve per-session data only for currently tracked live WebSocket sessions.
// Returns 1 and sets *out_pss on success, otherwise returns 0.
int websocket_get_live_pss(struct lws* wsi, struct per_session_data** out_pss);
// Actual relay port bound by libwebsockets at runtime (after fallback/retry logic)
extern int g_relay_port;
+18
View File
@@ -0,0 +1,18 @@
2026-04-01 09:57:22 - ==========================================
2026-04-01 09:57:22 - C-Relay Comprehensive Test Suite Runner
2026-04-01 09:57:22 - ==========================================
2026-04-01 09:57:22 - Relay URL: ws://127.0.0.1:8888
2026-04-01 09:57:22 - Log file: test_results_20260401_095722.log
2026-04-01 09:57:22 - Report file: test_report_20260401_095722.html
2026-04-01 09:57:22 -
2026-04-01 09:57:22 - Checking relay status at ws://127.0.0.1:8888...
2026-04-01 09:57:22 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
2026-04-01 09:57:22 -
2026-04-01 09:57:22 - Starting comprehensive test execution...
2026-04-01 09:57:22 -
2026-04-01 09:57:22 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
2026-04-01 09:57:22 - ==========================================
2026-04-01 09:57:22 - Running Test Suite: SQL Injection Tests
2026-04-01 09:57:22 - Description: Comprehensive SQL injection vulnerability testing
2026-04-01 09:57:22 - ==========================================
2026-04-01 09:57:22 - \033[0;31mERROR: Test script sql_injection_tests.sh not found\033[0m
+18
View File
@@ -0,0 +1,18 @@
2026-04-01 11:01:47 - ==========================================
2026-04-01 11:01:47 - C-Relay Comprehensive Test Suite Runner
2026-04-01 11:01:47 - ==========================================
2026-04-01 11:01:47 - Relay URL: ws://127.0.0.1:8888
2026-04-01 11:01:47 - Log file: test_results_20260401_110147.log
2026-04-01 11:01:47 - Report file: test_report_20260401_110147.html
2026-04-01 11:01:47 -
2026-04-01 11:01:47 - Checking relay status at ws://127.0.0.1:8888...
2026-04-01 11:01:47 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
2026-04-01 11:01:47 -
2026-04-01 11:01:47 - Starting comprehensive test execution...
2026-04-01 11:01:47 -
2026-04-01 11:01:47 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
2026-04-01 11:01:47 - ==========================================
2026-04-01 11:01:47 - Running Test Suite: SQL Injection Tests
2026-04-01 11:01:47 - Description: Comprehensive SQL injection vulnerability testing
2026-04-01 11:01:47 - ==========================================
2026-04-01 11:01:47 - \033[0;31mERROR: Test script sql_injection_tests.sh not found\033[0m
+16 -12
View File
@@ -8,7 +8,7 @@ set -e
# Configuration
RELAY_HOST="127.0.0.1"
RELAY_PORT="8888"
TEST_TIMEOUT=5
TEST_TIMEOUT=10
# Colors for output
RED='\033[0;31m'
@@ -34,7 +34,7 @@ test_websocket_message() {
# Send message via websocat and capture response
local response
response=$(echo "$message" | timeout $TEST_TIMEOUT websocat -B 1048576 ws://$RELAY_HOST:$RELAY_PORT 2>/dev/null || echo 'CONNECTION_FAILED')
response=$(echo "$message" | timeout $TEST_TIMEOUT websocat -B 1048576 ws://$RELAY_HOST:$RELAY_PORT 2>/dev/null | head -1 || echo 'CONNECTION_FAILED')
if [[ "$response" == "CONNECTION_FAILED" ]]; then
echo -e "${RED}FAILED${NC} - Could not connect to relay"
@@ -42,8 +42,8 @@ test_websocket_message() {
return 1
fi
if [[ "$response" == "TIMEOUT" ]]; then
echo -e "${RED}FAILED${NC} - Connection timeout"
if [[ -z "$response" ]]; then
echo -e "${RED}FAILED${NC} - Empty response"
FAILED_TESTS=$((FAILED_TESTS + 1))
return 1
fi
@@ -119,8 +119,9 @@ test_websocket_message "Invalid author type" '["REQ","sub1",{"authors":[123]}]'
# Test 6: Invalid author hex
test_websocket_message "Invalid author hex" '["REQ","sub1",{"authors":["invalid-hex"]}]' "error: invalid author hex string"
# Test 7: Too many authors
test_websocket_message "Too many authors" '["REQ","sub1",{"authors":["a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"]}]' "error: too many authors"
# Test 7: Too many authors (exceed MAX_AUTHORS_PER_FILTER with compact payload)
too_many_authors=$(yes '"a"' | head -n 1001 | paste -sd, -)
test_websocket_message "Too many authors" "[\"REQ\",\"sub1\",{\"authors\":[${too_many_authors}]}]" "error: too many authors"
echo
echo "=== Testing IDs Validation ==="
@@ -131,8 +132,9 @@ test_websocket_message "Invalid ID type" '["REQ","sub1",{"ids":[123]}]' "error:
# Test 9: Invalid ID hex
test_websocket_message "Invalid ID hex" '["REQ","sub1",{"ids":["invalid-hex"]}]' "error: invalid id hex string"
# Test 10: Too many IDs
test_websocket_message "Too many IDs" '["REQ","sub1",{"ids":["a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a","a"]}]' "error: too many ids"
# Test 10: Too many IDs (exceed MAX_IDS_PER_FILTER with compact payload)
too_many_ids=$(yes '"a"' | head -n 1001 | paste -sd, -)
test_websocket_message "Too many IDs" "[\"REQ\",\"sub1\",{\"ids\":[${too_many_ids}]}]" "error: too many ids"
echo
echo "=== Testing Kinds Validation ==="
@@ -146,8 +148,9 @@ test_websocket_message "Negative kind" '["REQ","sub1",{"kinds":[-1]}]' "error: i
# Test 13: Too large kind
test_websocket_message "Too large kind" '["REQ","sub1",{"kinds":[70000]}]' "error: invalid kind value"
# Test 14: Too many kinds
test_websocket_message "Too many kinds" '["REQ","sub1",{"kinds":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52]}]' "error: too many kinds"
# Test 14: Too many kinds (exceed MAX_KINDS_PER_FILTER=500)
too_many_kinds=$(seq -s, 1 501)
test_websocket_message "Too many kinds" "[\"REQ\",\"sub1\",{\"kinds\":[${too_many_kinds}]}]" "error: too many kinds"
echo
echo "=== Testing Timestamp Validation ==="
@@ -194,8 +197,9 @@ echo "=== Testing Tag Filter Validation ==="
# Test 25: Invalid tag filter type
test_websocket_message "Invalid tag filter type" '["REQ","sub1",{"#e":"not-an-array"}]' "error: #e must be an array"
# Test 26: Too many tag values
test_websocket_message "Too many tag values" '["REQ","sub1",{"#e":['$(printf '"a%.0s",' {1..101})'"a"]}]' "error: too many #e values"
# Test 26: Too many tag values (exceed MAX_TAG_VALUES_PER_FILTER=1000)
too_many_tag_values=$(yes '"a"' | head -n 1001 | paste -sd, -)
test_websocket_message "Too many tag values" "[\"REQ\",\"sub1\",{\"#e\":[${too_many_tag_values}]}]" "error: too many #e values"
# Test 27: Tag value too long
test_websocket_message "Tag value too long" '["REQ","sub1",{"#e":["'$(printf 'a%.0s' {1..1025})'"]}]' "error: #e value too long"
+26 -26
View File
@@ -1,5 +1,5 @@
=== NIP-42 Authentication Test Started ===
2026-04-01 05:46:11 - Starting NIP-42 authentication tests
2026-04-01 10:00:44 - Starting NIP-42 authentication tests
[INFO] === Starting NIP-42 Authentication Tests ===
[INFO] Checking dependencies...
[WARNING] wscat not found. Some manual WebSocket tests will be skipped
@@ -7,7 +7,7 @@
[SUCCESS] Dependencies check complete
[INFO] Test 1: Checking NIP-42 support in relay info
[SUCCESS] NIP-42 is advertised in supported NIPs
2026-04-01 05:46:11 - Supported NIPs: 1,2,4,9,11,12,13,15,16,20,22,33,40,42,50,70
2026-04-01 10:00:44 - Supported NIPs: 1,2,4,9,11,12,13,15,16,20,22,33,40,42,50,70
[INFO] Test 2: Testing AUTH challenge generation
[WARNING] Could not extract admin private key from relay.log - using manual test approach
[INFO] Manual test: Connect to relay and send an event without auth to trigger challenge
@@ -15,8 +15,8 @@
[INFO] Generated test keypair: test_pubkey
[INFO] Attempting to publish event without authentication...
[INFO] Publishing test event to relay...
2026-04-01 05:46:12 - Event publish result: connecting to localhost:8888... ok.
{"kind":1,"id":"334754e42c2bd54bdf733fb2c9a28c1e0f33d2275ca2c70f26062187793b37fe","pubkey":"40d9f28055baeba6a9bcac1942a17370add58a95a46985f66c5c14d485573983","created_at":1775036772,"tags":[],"content":"NIP-42 test event - should require auth","sig":"6e81c7335d34a770c72566250ac363aa5a126383a1efe2effe5b4b7e84bf600c4d9531b26e9be188fe884e9bd32aa384d49739815c7fba4594174277b90cbf7d"}
2026-04-01 10:00:45 - Event publish result: connecting to localhost:8888... ok.
{"kind":1,"id":"28174ed72f9ef5484a6596db6c6be181d2ed10e46d8c22a9f7a267becbc9a47a","pubkey":"b5f9adc3b362c0e81569a46648a3996708d6b47754d36c617683f18e1f1daf6b","created_at":1775052045,"tags":[],"content":"NIP-42 test event - should require auth","sig":"0041347b233a21d6542a263634f52f8ca7e3b5ecf07bc37c86f390f1a7ab0bc2051306b14aa70edff020bd7311b259d03c2c35a035b285f9e19051a924546666"}
publishing to ws://localhost:8888... success.
[SUCCESS] Relay requested authentication as expected
[INFO] Test 4: Testing WebSocket AUTH message handling
@@ -26,50 +26,50 @@ publishing to ws://localhost:8888... success.
[WARNING] Could not retrieve configuration events
[INFO] Test 6: Testing NIP-42 performance and stability
[INFO] Testing multiple authentication attempts...
2026-04-01 05:46:13 - Attempt 1: .187311847s - connecting to localhost:8888... ok.
{"kind":1,"id":"470a74d32e1b739e6bfd275c0d03ab1508fb5908c0fa01523e4a3bdce6a95db3","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036773,"tags":[],"content":"Performance test event 1","sig":"0c30a125cb67d336413f006a5dfa422d788e177254e496b966854bf45183238d23f6575dba37dec6fa46b6f6b906a25afafcbbcaa7d0c15add89dd7fd1a80c22"}
2026-04-01 10:00:46 - Attempt 1: .195528002s - connecting to localhost:8888... ok.
{"kind":1,"id":"72526c57e50d721bfcdcca23856f2c5d3021c3100f0121538d9d829042e9b8b5","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052046,"tags":[],"content":"Performance test event 1","sig":"1a10db86a39aaf47bda6d0cca5f888691bae32bfd85df8c178dddbf24861503a491bfe4da8d66dfe3e5e0afec7bf8644e7eb69c9f051c601673ae13284f9ab31"}
publishing to ws://localhost:8888... success.
2026-04-01 05:46:13 - Attempt 2: .185565503s - connecting to localhost:8888... ok.
{"kind":1,"id":"e4f9db77d30d8e71ef01b16dcb1179d03462aaf368aae30120898f3758a9d0b9","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036773,"tags":[],"content":"Performance test event 2","sig":"24a8f38c2cb230d95120e60a246a1a03ac17d4d195d25f2ae0b2f674c3383287eca53cd7fc61bc3cdc58433a3d2c2108558f4f1a98dcfc02a97b4ec8c537cdde"}
2026-04-01 10:00:46 - Attempt 2: .185198180s - connecting to localhost:8888... ok.
{"kind":1,"id":"84eeb907205806bb88fc353bb83f1bf0149f3c93c18695306d8480d36752878b","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052046,"tags":[],"content":"Performance test event 2","sig":"e568dbca3113966f61e69eff05576d296fac42b5ff2ab7341778acc02cf6e9440b59bf651a8a2995d89180a62ec20ffc1143e211cf2960c5abaa358a82992ac6"}
publishing to ws://localhost:8888... success.
2026-04-01 05:46:14 - Attempt 3: .185110183s - connecting to localhost:8888... ok.
{"kind":1,"id":"4a85e466756452e90358fe5d98fa41bda93b6bc77cb079bb1616ca8873f4fca1","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036774,"tags":[],"content":"Performance test event 3","sig":"72ab716ff3b4786309fd4cef27ae8cd9df1e3c1746b5e49ba45c070cafcfb72a5b7ba045e7d798f23b5d82989526b4d3c818509f0eec639c8ad265be85a4728e"}
2026-04-01 10:00:47 - Attempt 3: .197159540s - connecting to localhost:8888... ok.
{"kind":1,"id":"0874716ba0b2a845d379e703226513f90db6228ce958369f4dce5aff3df64c5a","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052046,"tags":[],"content":"Performance test event 3","sig":"e74e885eb464998bddf655c24c95a18d7299908c63fb82a47f8d14af65992f90e73794a9f289b550bc1dec1c0298112d2b25941be7e26e3f17dd66b4c5dc742e"}
publishing to ws://localhost:8888... success.
2026-04-01 05:46:14 - Attempt 4: .185725746s - connecting to localhost:8888... ok.
{"kind":1,"id":"4c05b6359da88f85c0aa4c94a296a465f71bddaa10d849ed0923f972ac0628a9","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036774,"tags":[],"content":"Performance test event 4","sig":"3b0c8bd1f3d965a259a0ab228ea4c47defef3ad563ebce860ec65ab0ee341c5305c0988ac88651664d3090ce2ff282a0fe4913231448158c423a74b6e3b2a7a9"}
2026-04-01 10:00:47 - Attempt 4: .202520319s - connecting to localhost:8888... ok.
{"kind":1,"id":"5614d1b795cd028e096720418bb4dbc64870d442c6295b9bb2931564a332099a","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052047,"tags":[],"content":"Performance test event 4","sig":"1c0645a791ab9df573b152b782884bcb5ec2a18b8f4e1c18d7dc2175977b7d1d572674169ea1d2b3e3fa8d228de90f6cc281d56a3ef471ba4f9510e920ed586e"}
publishing to ws://localhost:8888... success.
2026-04-01 05:46:15 - Attempt 5: .184467365s - connecting to localhost:8888... ok.
{"kind":1,"id":"0902e868c5e7909f0143fde7738d5e8f4c42ea0ced2c69a404cfeb6cd686ceaa","pubkey":"59d792f5cfd57b3a284ce769b5d4b1e8a6eb231c63239cdad7f07d791433e79a","created_at":1775036774,"tags":[],"content":"Performance test event 5","sig":"a0754e774e5bc3514dc4cf04ec51cab077413251cfce480e80badccc7660bfe8d1eac87d43c6e332d936cc171d24e8318e1ef5df7425a8218e01af661f291c47"}
2026-04-01 10:00:47 - Attempt 5: .222530325s - connecting to localhost:8888... ok.
{"kind":1,"id":"9139f1eb39c31c43cb9f02e60a0f75a0f99f6c7d9693a69a225c8b3e530f774a","pubkey":"29af457d7eea750c11c836dfbeed3076fa0bf1f1b67a404ee4c25733d9b54238","created_at":1775052047,"tags":[],"content":"Performance test event 5","sig":"bd245a7629c33619c0ed426f631e0c2413adb4488c281bea09886ceb6407b40e247d42f82e374e2f53ee5281f0d8361661a4c613c6414c59d09e5bb23eeeb02d"}
publishing to ws://localhost:8888... success.
[SUCCESS] Performance test completed: 5/5 successful responses
[INFO] Test 7: Testing kind-specific NIP-42 authentication requirements
[INFO] Generated test keypair for kind-specific tests: test_pubkey
[INFO] Testing kind 1 event (regular note) - should work without authentication...
2026-04-01 05:46:15 - Kind 1 event result: connecting to localhost:8888... ok.
{"kind":1,"id":"a1d9545c4ac16c2ab1fae4d5d77cabd368d765533b6f6d1f4dbb3c7859696a5e","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036775,"tags":[],"content":"Regular note - should not require auth","sig":"520796b21690bf03b20da28f30ffc84c9c198d0a908c07540f232f789c7a0327861e1f580d499be15b244bdf66c5c4b1cdf621bfdc5f60b1313d7f40036ffab8"}
2026-04-01 10:00:48 - Kind 1 event result: connecting to localhost:8888... ok.
{"kind":1,"id":"def386c66576a91f144a7321e9520a3ed46f9fecc56d9e79d32a162f249cace9","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052048,"tags":[],"content":"Regular note - should not require auth","sig":"7ee8fbf45e96d040df1a688e9bf028faaa49b81b26eeae6452b3a32cd9fd941c27b41734f14268d3deef14dbb19e514d29789211fffb27bfa9b73d20cddcc83e"}
publishing to ws://localhost:8888... success.
[SUCCESS] Kind 1 event accepted without authentication (correct behavior)
[INFO] Testing kind 4 event (direct message) - should require authentication...
2026-04-01 05:46:25 - Kind 4 event result: connecting to localhost:8888... ok.
{"kind":4,"id":"c047f5a9fb6ff46828d0bb862e4bcc32120e50fc7804d651485e2cdbddaf993a","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036775,"tags":[["p,test_pubkey"]],"content":"This is a direct message - should require auth","sig":"a7a027c302faf75e269d866a1b0137f2fbc02643d76435dc68707d2ba6560af95df709787f4a1a1bc7c1b9951615ad081a97cd7f694da94d5606d17100f0b0dc"}
2026-04-01 10:00:58 - Kind 4 event result: connecting to localhost:8888... ok.
{"kind":4,"id":"a2f2d176a835d17a0c9229c0ce3b1ba61a5eb81308e4a3b3446634f468721238","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052048,"tags":[["p,test_pubkey"]],"content":"This is a direct message - should require auth","sig":"441f9caba6ebcec6b6c55d13ffd9571f3fe441a54ddd0692f2868429a5087378d791c97289b8b990560dcf40f5c66e86ba6b41f1de78220a5f389c1f119bb887"}
publishing to ws://localhost:8888...
[SUCCESS] Kind 4 event requested authentication (correct behavior for DMs)
[INFO] Testing kind 14 event (chat message) - should require authentication...
2026-04-01 05:46:36 - Kind 14 event result: connecting to localhost:8888... ok.
{"kind":14,"id":"73e50afdaefdbb01ed021f8c888a87b00aa59e4d68c124101353fabcdfa59ae1","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036786,"tags":[["p,test_pubkey"]],"content":"Chat message - should require auth","sig":"ec2c713e2add876856e6cb0d1d9790c43525c5139a0fe06fbee71898f1042173dc3af9187e6fbccfef7406c1af2f218631a1cdcb7ad73e34553cdf3aecc4f209"}
2026-04-01 10:01:09 - Kind 14 event result: connecting to localhost:8888... ok.
{"kind":14,"id":"e234a6faa63f95cd24f8596fbfac573730533bb0abcde47b4d341ff0acace657","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052059,"tags":[["p,test_pubkey"]],"content":"Chat message - should require auth","sig":"fd6ba0ddaa7e2379aa49c5cda6599d0a38264799b228a8e907b948bef645080603f7f910c587f6cfb1ef1bac946c47dc681fdab6abd248376cd552bcd535ef9e"}
publishing to ws://localhost:8888...
[SUCCESS] Kind 14 event requested authentication (correct behavior for DMs)
[INFO] Testing other event kinds - should work without authentication...
2026-04-01 05:46:36 - Kind 0 event result: connecting to localhost:8888... ok.
{"kind":0,"id":"51fe1630721cfa3472ba7c3af911a3164c788a48c6a678179c62be48ac3d1f1c","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036796,"tags":[],"content":"Test event kind 0 - should not require auth","sig":"d72d827742fdc86daf1389c8dad40efc56c22b9744ad23547ea0b0a37598e5c18ccdac20b9ef2e6adf46d7683bff19f2cbc11fb00a252903efc1076c8f613b38"}
2026-04-01 10:01:09 - Kind 0 event result: connecting to localhost:8888... ok.
{"kind":0,"id":"d87b69e8a2c8f6d98229b55cde3f0fe1b70c6cafd080e4462209849338eafc76","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052069,"tags":[],"content":"Test event kind 0 - should not require auth","sig":"e3222bd4882c881d039e6790a6a8d9f8e20a71d0f9d5089e98604acaa4feff7c1b1b2d9b4171d640da3bba955408a9391a0fd52334cc4a4165fa932e5163f9dd"}
publishing to ws://localhost:8888... success.
[SUCCESS] Kind 0 event accepted without authentication (correct)
2026-04-01 05:46:36 - Kind 3 event result: connecting to localhost:8888... ok.
{"kind":3,"id":"bf4155eb8b54bdd73a0b64365666caf886433634693cc3c4ab1d586187fc6bae","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036796,"tags":[],"content":"Test event kind 3 - should not require auth","sig":"d27f654ccc9f95e76aacafcce7d42528545fa90b77d422a17301de6931883d8c00a0a84af56ab5d37a0f425140dbfc227458557f9050597beec42cacd0a15864"}
2026-04-01 10:01:09 - Kind 3 event result: connecting to localhost:8888... ok.
{"kind":3,"id":"ac0d45d7db2818bb5e674d4760711349928cf3d5dc8f1fc3717d9e7274e26fb0","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052069,"tags":[],"content":"Test event kind 3 - should not require auth","sig":"b65b317c81bc733ee2ff5921430b34d722e8d177147c5d1cddf14036573223582a3b7232810fc13169eb7a61ab3b99cd37ce13991673fce142d1ea10c702305e"}
publishing to ws://localhost:8888... success.
[SUCCESS] Kind 3 event accepted without authentication (correct)
2026-04-01 05:46:37 - Kind 7 event result: connecting to localhost:8888... ok.
{"kind":7,"id":"658e0caf1c4b52062363849d55312070005124e8a10202df285078222e523132","pubkey":"06ff6b7ca3c72cc7cebb9fe70ea76b6f79b6226c4dc5adc4ebcc59220c23c9bf","created_at":1775036797,"tags":[],"content":"Test event kind 7 - should not require auth","sig":"50361b3aa6b85747befe20e50e317d8f905128930cec0263494434ec9029089d880ff23cdf071c5b63b8417eff216658f1229c0f0dc5174a9a16a6d5fe38fd4d"}
2026-04-01 10:01:10 - Kind 7 event result: connecting to localhost:8888... ok.
{"kind":7,"id":"ad7e77948a4742abfca2d63c5bb748f3db3940c85841f521b3b7d634bf09955a","pubkey":"7c9b00749c966e50d18d4b807b44d4612bf9a8f67925fac3f00d21918b6e7f9c","created_at":1775052070,"tags":[],"content":"Test event kind 7 - should not require auth","sig":"8a5e5a03956cec04d56934314a0876fccbd221546432aaccdbc249b5f9cedc221219abf6e2856b98675de205afaa0e91ea642933ec6724576a40db43d17aa758"}
publishing to ws://localhost:8888... success.
[SUCCESS] Kind 7 event accepted without authentication (correct)
[INFO] Kind-specific authentication test completed
+392
View File
@@ -0,0 +1,392 @@
2026-04-01 09:11:07 - ==========================================
2026-04-01 09:11:07 - C-Relay Comprehensive Test Suite Runner
2026-04-01 09:11:07 - ==========================================
2026-04-01 09:11:07 - Relay URL: ws://127.0.0.1:8888
2026-04-01 09:11:07 - Log file: test_results_20260401_091107.log
2026-04-01 09:11:07 - Report file: test_report_20260401_091107.html
2026-04-01 09:11:07 -
2026-04-01 09:11:07 - Checking relay status at ws://127.0.0.1:8888...
2026-04-01 09:11:07 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
2026-04-01 09:11:07 -
2026-04-01 09:11:07 - Starting comprehensive test execution...
2026-04-01 09:11:07 -
2026-04-01 09:11:07 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
2026-04-01 09:11:07 - ==========================================
2026-04-01 09:11:07 - Running Test Suite: SQL Injection Tests
2026-04-01 09:11:07 - Description: Comprehensive SQL injection vulnerability testing
2026-04-01 09:11:07 - ==========================================
==========================================
C-Relay SQL Injection Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Valid query works
=== Authors Filter SQL Injection Tests ===
Testing Authors filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== IDs Filter SQL Injection Tests ===
Testing IDs filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Kinds Filter SQL Injection Tests ===
Testing Kinds filter with string injection... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with negative value... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with very large value... PASSED - SQL injection blocked (rejected with error)
=== Search Filter SQL Injection Tests ===
Testing Search filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Tag Filter SQL Injection Tests ===
Testing #e tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
=== Timestamp Filter SQL Injection Tests ===
Testing Since parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Until parameter injection... PASSED - SQL injection blocked (rejected with error)
=== Limit Parameter SQL Injection Tests ===
Testing Limit parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Limit with UNION... PASSED - SQL injection blocked (rejected with error)
=== Complex Multi-Filter SQL Injection Tests ===
Testing Multi-filter with authors injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with search injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with tag injection... PASSED - SQL injection blocked (query sanitized)
=== COUNT Message SQL Injection Tests ===
Testing COUNT with authors payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: #... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: #... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Edge Case SQL Injection Tests ===
Testing Empty string injection... PASSED - SQL injection blocked (rejected with error)
Testing Null byte injection... PASSED - SQL injection blocked (silently rejected)
Testing Unicode injection... PASSED - SQL injection blocked (rejected with error)
Testing Very long injection payload... PASSED - SQL injection blocked (rejected with error)
=== Subscription ID SQL Injection Tests ===
Testing Subscription ID injection... PASSED - SQL injection blocked (rejected with error)
Testing Subscription ID with quotes... PASSED - SQL injection blocked (silently rejected)
=== CLOSE Message SQL Injection Tests ===
Testing CLOSE with injection... PASSED - SQL injection blocked (rejected with error)
=== Test Results ===
Total tests: 318
Passed: 318
Failed: 0
✓ All SQL injection tests passed!
The relay appears to be protected against SQL injection attacks.
2026-04-01 09:11:14 - \033[0;32m✓ SQL Injection Tests PASSED\033[0m (Duration: 7s)
2026-04-01 09:11:14 - ==========================================
2026-04-01 09:11:14 - Running Test Suite: Filter Validation Tests
2026-04-01 09:11:14 - Description: Input validation for REQ and COUNT messages
2026-04-01 09:11:14 - ==========================================
=== C-Relay Filter Validation Tests ===
Testing against relay at ws://127.0.0.1:8888
Testing Valid REQ message... PASSED
Testing Valid COUNT message... PASSED
=== Testing Filter Array Validation ===
Testing Non-object filter... PASSED
Testing Too many filters... PASSED
=== Testing Authors Validation ===
Testing Invalid author type... PASSED
Testing Invalid author hex... PASSED
Testing Too many authors... FAILED - Expected error 'error: too many authors', got: ["NOTICE", "error: invalid author hex string"]
2026-04-01 09:11:14 - \033[0;31m✗ Filter Validation Tests FAILED\033[0m (Duration: 0s)
+733
View File
@@ -0,0 +1,733 @@
2026-04-01 09:57:29 - ==========================================
2026-04-01 09:57:30 - C-Relay Comprehensive Test Suite Runner
2026-04-01 09:57:30 - ==========================================
2026-04-01 09:57:30 - Relay URL: ws://127.0.0.1:8888
2026-04-01 09:57:30 - Log file: test_results_20260401_095729.log
2026-04-01 09:57:30 - Report file: test_report_20260401_095729.html
2026-04-01 09:57:30 -
2026-04-01 09:57:30 - Checking relay status at ws://127.0.0.1:8888...
2026-04-01 09:57:30 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
2026-04-01 09:57:30 -
2026-04-01 09:57:30 - Starting comprehensive test execution...
2026-04-01 09:57:30 -
2026-04-01 09:57:30 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
2026-04-01 09:57:30 - ==========================================
2026-04-01 09:57:30 - Running Test Suite: SQL Injection Tests
2026-04-01 09:57:30 - Description: Comprehensive SQL injection vulnerability testing
2026-04-01 09:57:30 - ==========================================
==========================================
C-Relay SQL Injection Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Valid query works
=== Authors Filter SQL Injection Tests ===
Testing Authors filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== IDs Filter SQL Injection Tests ===
Testing IDs filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Kinds Filter SQL Injection Tests ===
Testing Kinds filter with string injection... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with negative value... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with very large value... PASSED - SQL injection blocked (rejected with error)
=== Search Filter SQL Injection Tests ===
Testing Search filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Tag Filter SQL Injection Tests ===
Testing #e tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
=== Timestamp Filter SQL Injection Tests ===
Testing Since parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Until parameter injection... PASSED - SQL injection blocked (rejected with error)
=== Limit Parameter SQL Injection Tests ===
Testing Limit parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Limit with UNION... PASSED - SQL injection blocked (rejected with error)
=== Complex Multi-Filter SQL Injection Tests ===
Testing Multi-filter with authors injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with search injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with tag injection... PASSED - SQL injection blocked (query sanitized)
=== COUNT Message SQL Injection Tests ===
Testing COUNT with authors payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: #... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: #... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Edge Case SQL Injection Tests ===
Testing Empty string injection... PASSED - SQL injection blocked (rejected with error)
Testing Null byte injection... PASSED - SQL injection blocked (silently rejected)
Testing Unicode injection... PASSED - SQL injection blocked (rejected with error)
Testing Very long injection payload... PASSED - SQL injection blocked (rejected with error)
=== Subscription ID SQL Injection Tests ===
Testing Subscription ID injection... PASSED - SQL injection blocked (rejected with error)
Testing Subscription ID with quotes... PASSED - SQL injection blocked (silently rejected)
=== CLOSE Message SQL Injection Tests ===
Testing CLOSE with injection... PASSED - SQL injection blocked (rejected with error)
=== Test Results ===
Total tests: 318
Passed: 318
Failed: 0
✓ All SQL injection tests passed!
The relay appears to be protected against SQL injection attacks.
2026-04-01 09:57:36 - \033[0;32m✓ SQL Injection Tests PASSED\033[0m (Duration: 6s)
2026-04-01 09:57:36 - ==========================================
2026-04-01 09:57:36 - Running Test Suite: Filter Validation Tests
2026-04-01 09:57:36 - Description: Input validation for REQ and COUNT messages
2026-04-01 09:57:36 - ==========================================
=== C-Relay Filter Validation Tests ===
Testing against relay at ws://127.0.0.1:8888
Testing Valid REQ message... PASSED
Testing Valid COUNT message... PASSED
=== Testing Filter Array Validation ===
Testing Non-object filter... PASSED
Testing Too many filters... PASSED
=== Testing Authors Validation ===
Testing Invalid author type... PASSED
Testing Invalid author hex... PASSED
Testing Too many authors... PASSED
=== Testing IDs Validation ===
Testing Invalid ID type... PASSED
Testing Invalid ID hex... PASSED
Testing Too many IDs... PASSED
=== Testing Kinds Validation ===
Testing Invalid kind type... PASSED
Testing Negative kind... PASSED
Testing Too large kind... PASSED
Testing Too many kinds... PASSED
=== Testing Timestamp Validation ===
Testing Invalid since type... PASSED
Testing Negative since... PASSED
Testing Invalid until type... PASSED
Testing Negative until... PASSED
=== Testing Limit Validation ===
Testing Invalid limit type... PASSED
Testing Negative limit... PASSED
Testing Too large limit... PASSED
=== Testing Search Validation ===
Testing Invalid search type... PASSED
Testing Search too long... PASSED
Testing Search SQL injection... PASSED
=== Testing Tag Filter Validation ===
Testing Invalid tag filter type... PASSED
Testing Too many tag values... PASSED
Testing Tag value too long... PASSED
=== Testing Rate Limiting ===
Testing rate limiting with malformed requests... UNCERTAIN - Rate limiting may not have triggered (this could be normal)
=== Test Results ===
Total tests: 28
Passed: 28
Failed: 0
All tests passed!
2026-04-01 09:57:39 - \033[0;32m✓ Filter Validation Tests PASSED\033[0m (Duration: 3s)
2026-04-01 09:57:39 - ==========================================
2026-04-01 09:57:39 - Running Test Suite: Subscription Validation Tests
2026-04-01 09:57:39 - Description: Subscription ID and message validation
2026-04-01 09:57:39 - ==========================================
Testing subscription ID validation fixes...
Testing malformed subscription IDs...
Empty ID test: Connection failed (expected)
Long ID test: Connection failed (expected)
Invalid chars test: Connection failed (expected)
NULL ID test: Connection failed (expected)
Valid ID test: Failed
Testing CLOSE message validation...
CLOSE empty ID test: Connection failed (expected)
CLOSE valid ID test: Failed
Subscription validation tests completed.
2026-04-01 09:57:39 - \033[0;32m✓ Subscription Validation Tests PASSED\033[0m (Duration: 0s)
2026-04-01 09:57:39 - ==========================================
2026-04-01 09:57:39 - Running Test Suite: Memory Corruption Tests
2026-04-01 09:57:39 - Description: Buffer overflow and memory safety testing
2026-04-01 09:57:39 - ==========================================
==========================================
C-Relay Memory Corruption Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
Note: These tests may cause the relay to crash if vulnerabilities exist
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - No memory corruption detected
=== Subscription ID Memory Corruption Tests ===
Testing Empty subscription ID... UNCERTAIN - Expected error but got normal response
Testing Very long subscription ID (1KB)... UNCERTAIN - Expected error but got normal response
Testing Very long subscription ID (10KB)... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with null bytes... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with special chars... UNCERTAIN - Expected error but got normal response
Testing Unicode subscription ID... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with path traversal... UNCERTAIN - Expected error but got normal response
=== Filter Array Memory Corruption Tests ===
Testing Too many filters (50)... UNCERTAIN - Expected error but got normal response
=== Concurrent Access Memory Tests ===
Testing Concurrent subscription creation... ["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
["EVENT","concurrent_1775051860259483357",{"pubkey":"4f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa","created_at":1775051826,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"791ebcdcbe58fd5153b9a1f8f39907b0fcc4d86fc189e35fc9b6365031339b53","sig":"4164e20b9169a57c8086a0430b81af9bcabf69082c1d0d9d869673f2f5b0a350fb0ce882f2a19f08b613a0872edb69a0f8d1ccba0675bacff16c19b7282080e6"}]
PASSED - Concurrent access handled safely
Testing Concurrent CLOSE operations...
PASSED - Concurrent access handled safely
=== Malformed JSON Memory Tests ===
Testing Unclosed JSON object... UNCERTAIN - Expected error but got normal response
Testing Mismatched brackets... UNCERTAIN - Expected error but got normal response
Testing Extra closing brackets... UNCERTAIN - Expected error but got normal response
Testing Null bytes in JSON... UNCERTAIN - Expected error but got normal response
=== Large Message Memory Tests ===
Testing Very large filter array... UNCERTAIN - Expected error but got normal response
Testing Very long search term... UNCERTAIN - Expected error but got normal response
=== Test Results ===
Total tests: 17
Passed: 17
Failed: 0
✓ All memory corruption tests passed!
The relay appears to handle memory safely.
2026-04-01 09:57:40 - \033[0;32m✓ Memory Corruption Tests PASSED\033[0m (Duration: 1s)
2026-04-01 09:57:40 - ==========================================
2026-04-01 09:57:40 - Running Test Suite: Input Validation Tests
2026-04-01 09:57:40 - Description: Comprehensive input boundary testing
2026-04-01 09:57:40 - ==========================================
==========================================
C-Relay Input Validation Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Input accepted correctly
=== Message Type Validation ===
Testing Invalid message type - string... PASSED - Invalid input properly rejected
Testing Invalid message type - number... PASSED - Invalid input properly rejected
Testing Invalid message type - null... PASSED - Invalid input properly rejected
Testing Invalid message type - object... PASSED - Invalid input properly rejected
Testing Empty message type... PASSED - Invalid input properly rejected
Testing Very long message type... PASSED - Invalid input properly rejected
=== Message Structure Validation ===
Testing Too few arguments... PASSED - Invalid input properly rejected
Testing Too many arguments... PASSED - Invalid input properly rejected
Testing Non-array message... PASSED - Invalid input properly rejected
Testing Empty array... PASSED - Invalid input properly rejected
Testing Nested arrays incorrectly... PASSED - Invalid input properly rejected
=== Subscription ID Boundary Tests ===
Testing Valid subscription ID... PASSED - Input accepted correctly
Testing Empty subscription ID... PASSED - Invalid input properly rejected
Testing Subscription ID with spaces... PASSED - Invalid input properly rejected
Testing Subscription ID with newlines... PASSED - Invalid input properly rejected
Testing Subscription ID with tabs... PASSED - Invalid input properly rejected
Testing Subscription ID with control chars... PASSED - Invalid input properly rejected
Testing Unicode subscription ID... PASSED - Invalid input properly rejected
Testing Very long subscription ID... PASSED - Invalid input properly rejected
=== Filter Object Validation ===
Testing Valid empty filter... PASSED - Input accepted correctly
Testing Non-object filter... PASSED - Invalid input properly rejected
Testing Null filter... PASSED - Invalid input properly rejected
Testing Array filter... PASSED - Invalid input properly rejected
Testing Filter with invalid keys... PASSED - Input accepted correctly
=== Authors Field Validation ===
Testing Valid authors array... PASSED - Input accepted correctly
Testing Empty authors array... PASSED - Input accepted correctly
Testing Non-array authors... PASSED - Invalid input properly rejected
Testing Invalid hex in authors... PASSED - Invalid input properly rejected
Testing Short pubkey in authors... PASSED - Invalid input properly rejected
=== IDs Field Validation ===
Testing Valid ids array... PASSED - Input accepted correctly
Testing Empty ids array... PASSED - Input accepted correctly
Testing Non-array ids... PASSED - Invalid input properly rejected
=== Kinds Field Validation ===
Testing Valid kinds array... PASSED - Input accepted correctly
Testing Empty kinds array... PASSED - Input accepted correctly
Testing Non-array kinds... PASSED - Invalid input properly rejected
Testing String in kinds... PASSED - Invalid input properly rejected
=== Timestamp Field Validation ===
Testing Valid since timestamp... PASSED - Input accepted correctly
Testing Valid until timestamp... PASSED - Input accepted correctly
Testing String since timestamp... PASSED - Invalid input properly rejected
Testing Negative timestamp... PASSED - Invalid input properly rejected
=== Limit Field Validation ===
Testing Valid limit... PASSED - Input accepted correctly
Testing Zero limit... PASSED - Input accepted correctly
Testing String limit... PASSED - Invalid input properly rejected
Testing Negative limit... PASSED - Invalid input properly rejected
=== Multiple Filters ===
Testing Two valid filters... PASSED - Input accepted correctly
Testing Many filters... PASSED - Input accepted correctly
=== Test Results ===
Total tests: 47
Passed: 47
Failed: 0
✓ All input validation tests passed!
The relay properly validates input.
2026-04-01 09:57:41 - \033[0;32m✓ Input Validation Tests PASSED\033[0m (Duration: 1s)
2026-04-01 09:57:41 -
2026-04-01 09:57:41 - \033[0;34m=== PERFORMANCE TEST SUITES ===\033[0m
2026-04-01 09:57:41 - ==========================================
2026-04-01 09:57:41 - Running Test Suite: Subscription Limit Tests
2026-04-01 09:57:41 - Description: Subscription limit enforcement testing
2026-04-01 09:57:41 - ==========================================
=== Subscription Limit Test ===
[INFO] Testing relay at: ws://127.0.0.1:8888
[INFO] Note: This test assumes default subscription limits (max 25 per client)
=== Test 1: Basic Connectivity ===
[INFO] Testing basic WebSocket connection...
[PASS] Basic connectivity works
=== Test 2: Subscription Limit Enforcement ===
[INFO] Testing subscription limits by creating multiple subscriptions...
[INFO] Creating multiple subscriptions within a single connection...
[INFO] Hit subscription limit at subscription 2
[PASS] Subscription limit enforcement working (limit hit after 1 subscriptions)
=== Test Complete ===
2026-04-01 09:57:42 - \033[0;32m✓ Subscription Limit Tests PASSED\033[0m (Duration: 1s)
2026-04-01 09:57:42 - ==========================================
2026-04-01 09:57:42 - Running Test Suite: Load Testing
2026-04-01 09:57:42 - Description: High concurrent connection testing
2026-04-01 09:57:42 - ==========================================
==========================================
C-Relay Load Testing Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
✓ Relay is accessible
==========================================
Load Test: Light Load Test
Description: Basic load test with moderate concurrent connections
Concurrent clients: 10
Messages per client: 5
==========================================
Launching 10 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 1s
Total connections attempted: 10
Successful connections: 10
Failed connections: 0
Connection success rate: 100%
Messages expected: 50
Messages sent: 50
Messages received: 100
✓ EXCELLENT: High connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Medium Load Test
Description: Moderate load test with higher concurrency
Concurrent clients: 25
Messages per client: 10
==========================================
Launching 25 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 4s
Total connections attempted: 35
Successful connections: 25
Failed connections: 0
Connection success rate: 71%
Messages expected: 250
Messages sent: 250
Messages received: 500
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Heavy Load Test
Description: Heavy load test with high concurrency
Concurrent clients: 50
Messages per client: 20
==========================================
Launching 50 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 15s
Total connections attempted: 85
Successful connections: 50
Failed connections: 0
Connection success rate: 58%
Messages expected: 1000
Messages sent: 1000
Messages received: 2000
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Stress Test
Description: Maximum load test to find breaking point
Concurrent clients: 100
Messages per client: 50
==========================================
Launching 100 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 64s
Total connections attempted: 185
Successful connections: 100
Failed connections: 0
Connection success rate: 54%
Messages expected: 5000
Messages sent: 5000
Messages received: 7500
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Testing Complete
==========================================
All load tests completed. Check individual test results above.
If any tests failed, the relay may need optimization or have resource limits.
2026-04-01 09:59:07 - \033[0;32m✓ Load Testing PASSED\033[0m (Duration: 85s)
2026-04-01 09:59:07 - ==========================================
2026-04-01 09:59:07 - Running Test Suite: Stress Testing
2026-04-01 09:59:07 - Description: Resource usage and stability testing
2026-04-01 09:59:07 - ==========================================
2026-04-01 09:59:07 - \033[0;31mERROR: Test script stress_tests.sh not found\033[0m
+733
View File
@@ -0,0 +1,733 @@
2026-04-01 11:02:00 - ==========================================
2026-04-01 11:02:00 - C-Relay Comprehensive Test Suite Runner
2026-04-01 11:02:00 - ==========================================
2026-04-01 11:02:00 - Relay URL: ws://127.0.0.1:8888
2026-04-01 11:02:00 - Log file: test_results_20260401_110200.log
2026-04-01 11:02:00 - Report file: test_report_20260401_110200.html
2026-04-01 11:02:00 -
2026-04-01 11:02:00 - Checking relay status at ws://127.0.0.1:8888...
2026-04-01 11:02:00 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
2026-04-01 11:02:00 -
2026-04-01 11:02:00 - Starting comprehensive test execution...
2026-04-01 11:02:00 -
2026-04-01 11:02:00 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
2026-04-01 11:02:00 - ==========================================
2026-04-01 11:02:00 - Running Test Suite: SQL Injection Tests
2026-04-01 11:02:00 - Description: Comprehensive SQL injection vulnerability testing
2026-04-01 11:02:00 - ==========================================
==========================================
C-Relay SQL Injection Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Valid query works
=== Authors Filter SQL Injection Tests ===
Testing Authors filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== IDs Filter SQL Injection Tests ===
Testing IDs filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Kinds Filter SQL Injection Tests ===
Testing Kinds filter with string injection... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with negative value... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with very large value... PASSED - SQL injection blocked (rejected with error)
=== Search Filter SQL Injection Tests ===
Testing Search filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Tag Filter SQL Injection Tests ===
Testing #e tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
=== Timestamp Filter SQL Injection Tests ===
Testing Since parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Until parameter injection... PASSED - SQL injection blocked (rejected with error)
=== Limit Parameter SQL Injection Tests ===
Testing Limit parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Limit with UNION... PASSED - SQL injection blocked (rejected with error)
=== Complex Multi-Filter SQL Injection Tests ===
Testing Multi-filter with authors injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with search injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with tag injection... PASSED - SQL injection blocked (query sanitized)
=== COUNT Message SQL Injection Tests ===
Testing COUNT with authors payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: #... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: #... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Edge Case SQL Injection Tests ===
Testing Empty string injection... PASSED - SQL injection blocked (rejected with error)
Testing Null byte injection... PASSED - SQL injection blocked (silently rejected)
Testing Unicode injection... PASSED - SQL injection blocked (rejected with error)
Testing Very long injection payload... PASSED - SQL injection blocked (rejected with error)
=== Subscription ID SQL Injection Tests ===
Testing Subscription ID injection... PASSED - SQL injection blocked (rejected with error)
Testing Subscription ID with quotes... PASSED - SQL injection blocked (silently rejected)
=== CLOSE Message SQL Injection Tests ===
Testing CLOSE with injection... PASSED - SQL injection blocked (rejected with error)
=== Test Results ===
Total tests: 318
Passed: 318
Failed: 0
✓ All SQL injection tests passed!
The relay appears to be protected against SQL injection attacks.
2026-04-01 11:02:07 - \033[0;32m✓ SQL Injection Tests PASSED\033[0m (Duration: 7s)
2026-04-01 11:02:07 - ==========================================
2026-04-01 11:02:07 - Running Test Suite: Filter Validation Tests
2026-04-01 11:02:07 - Description: Input validation for REQ and COUNT messages
2026-04-01 11:02:07 - ==========================================
=== C-Relay Filter Validation Tests ===
Testing against relay at ws://127.0.0.1:8888
Testing Valid REQ message... PASSED
Testing Valid COUNT message... PASSED
=== Testing Filter Array Validation ===
Testing Non-object filter... PASSED
Testing Too many filters... PASSED
=== Testing Authors Validation ===
Testing Invalid author type... PASSED
Testing Invalid author hex... PASSED
Testing Too many authors... PASSED
=== Testing IDs Validation ===
Testing Invalid ID type... PASSED
Testing Invalid ID hex... PASSED
Testing Too many IDs... PASSED
=== Testing Kinds Validation ===
Testing Invalid kind type... PASSED
Testing Negative kind... PASSED
Testing Too large kind... PASSED
Testing Too many kinds... PASSED
=== Testing Timestamp Validation ===
Testing Invalid since type... PASSED
Testing Negative since... PASSED
Testing Invalid until type... PASSED
Testing Negative until... PASSED
=== Testing Limit Validation ===
Testing Invalid limit type... PASSED
Testing Negative limit... PASSED
Testing Too large limit... PASSED
=== Testing Search Validation ===
Testing Invalid search type... PASSED
Testing Search too long... PASSED
Testing Search SQL injection... PASSED
=== Testing Tag Filter Validation ===
Testing Invalid tag filter type... PASSED
Testing Too many tag values... PASSED
Testing Tag value too long... PASSED
=== Testing Rate Limiting ===
Testing rate limiting with malformed requests... UNCERTAIN - Rate limiting may not have triggered (this could be normal)
=== Test Results ===
Total tests: 28
Passed: 28
Failed: 0
All tests passed!
2026-04-01 11:02:10 - \033[0;32m✓ Filter Validation Tests PASSED\033[0m (Duration: 3s)
2026-04-01 11:02:10 - ==========================================
2026-04-01 11:02:10 - Running Test Suite: Subscription Validation Tests
2026-04-01 11:02:10 - Description: Subscription ID and message validation
2026-04-01 11:02:10 - ==========================================
Testing subscription ID validation fixes...
Testing malformed subscription IDs...
Empty ID test: Connection failed (expected)
Long ID test: Connection failed (expected)
Invalid chars test: Connection failed (expected)
NULL ID test: Connection failed (expected)
Valid ID test: Failed
Testing CLOSE message validation...
CLOSE empty ID test: Connection failed (expected)
CLOSE valid ID test: Failed
Subscription validation tests completed.
2026-04-01 11:02:10 - \033[0;32m✓ Subscription Validation Tests PASSED\033[0m (Duration: 0s)
2026-04-01 11:02:10 - ==========================================
2026-04-01 11:02:10 - Running Test Suite: Memory Corruption Tests
2026-04-01 11:02:10 - Description: Buffer overflow and memory safety testing
2026-04-01 11:02:10 - ==========================================
==========================================
C-Relay Memory Corruption Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
Note: These tests may cause the relay to crash if vulnerabilities exist
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - No memory corruption detected
=== Subscription ID Memory Corruption Tests ===
Testing Empty subscription ID... UNCERTAIN - Expected error but got normal response
Testing Very long subscription ID (1KB)... UNCERTAIN - Expected error but got normal response
Testing Very long subscription ID (10KB)... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with null bytes... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with special chars... UNCERTAIN - Expected error but got normal response
Testing Unicode subscription ID... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with path traversal... UNCERTAIN - Expected error but got normal response
=== Filter Array Memory Corruption Tests ===
Testing Too many filters (50)... UNCERTAIN - Expected error but got normal response
=== Concurrent Access Memory Tests ===
Testing Concurrent subscription creation... ["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
["EVENT","concurrent_1775055730660028379",{"pubkey":"26a8961265cc6db56d18b95fb54ad16a54259ab4342a2bd83e102846cb120e94","created_at":1775055645,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"f5ac528fb84b75bdef8050a56f0c2b4733389b2d97f91344811a7c3bf7e0bc4d","sig":"8c7480c64a4db80b643f60d4d6d38b5585bd6b10da3dbee0eab7b60796ee18826400162242c7056782a410de0b119860ba3210bffba78f0f6be466634e3b6940"}]
PASSED - Concurrent access handled safely
Testing Concurrent CLOSE operations...
PASSED - Concurrent access handled safely
=== Malformed JSON Memory Tests ===
Testing Unclosed JSON object... UNCERTAIN - Expected error but got normal response
Testing Mismatched brackets... UNCERTAIN - Expected error but got normal response
Testing Extra closing brackets... UNCERTAIN - Expected error but got normal response
Testing Null bytes in JSON... UNCERTAIN - Expected error but got normal response
=== Large Message Memory Tests ===
Testing Very large filter array... UNCERTAIN - Expected error but got normal response
Testing Very long search term... UNCERTAIN - Expected error but got normal response
=== Test Results ===
Total tests: 17
Passed: 17
Failed: 0
✓ All memory corruption tests passed!
The relay appears to handle memory safely.
2026-04-01 11:02:11 - \033[0;32m✓ Memory Corruption Tests PASSED\033[0m (Duration: 1s)
2026-04-01 11:02:11 - ==========================================
2026-04-01 11:02:11 - Running Test Suite: Input Validation Tests
2026-04-01 11:02:11 - Description: Comprehensive input boundary testing
2026-04-01 11:02:11 - ==========================================
==========================================
C-Relay Input Validation Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Input accepted correctly
=== Message Type Validation ===
Testing Invalid message type - string... PASSED - Invalid input properly rejected
Testing Invalid message type - number... PASSED - Invalid input properly rejected
Testing Invalid message type - null... PASSED - Invalid input properly rejected
Testing Invalid message type - object... PASSED - Invalid input properly rejected
Testing Empty message type... PASSED - Invalid input properly rejected
Testing Very long message type... PASSED - Invalid input properly rejected
=== Message Structure Validation ===
Testing Too few arguments... PASSED - Invalid input properly rejected
Testing Too many arguments... PASSED - Invalid input properly rejected
Testing Non-array message... PASSED - Invalid input properly rejected
Testing Empty array... PASSED - Invalid input properly rejected
Testing Nested arrays incorrectly... PASSED - Invalid input properly rejected
=== Subscription ID Boundary Tests ===
Testing Valid subscription ID... PASSED - Input accepted correctly
Testing Empty subscription ID... PASSED - Invalid input properly rejected
Testing Subscription ID with spaces... PASSED - Invalid input properly rejected
Testing Subscription ID with newlines... PASSED - Invalid input properly rejected
Testing Subscription ID with tabs... PASSED - Invalid input properly rejected
Testing Subscription ID with control chars... PASSED - Invalid input properly rejected
Testing Unicode subscription ID... PASSED - Invalid input properly rejected
Testing Very long subscription ID... PASSED - Invalid input properly rejected
=== Filter Object Validation ===
Testing Valid empty filter... PASSED - Input accepted correctly
Testing Non-object filter... PASSED - Invalid input properly rejected
Testing Null filter... PASSED - Invalid input properly rejected
Testing Array filter... PASSED - Invalid input properly rejected
Testing Filter with invalid keys... PASSED - Input accepted correctly
=== Authors Field Validation ===
Testing Valid authors array... PASSED - Input accepted correctly
Testing Empty authors array... PASSED - Input accepted correctly
Testing Non-array authors... PASSED - Invalid input properly rejected
Testing Invalid hex in authors... PASSED - Invalid input properly rejected
Testing Short pubkey in authors... PASSED - Invalid input properly rejected
=== IDs Field Validation ===
Testing Valid ids array... PASSED - Input accepted correctly
Testing Empty ids array... PASSED - Input accepted correctly
Testing Non-array ids... PASSED - Invalid input properly rejected
=== Kinds Field Validation ===
Testing Valid kinds array... PASSED - Input accepted correctly
Testing Empty kinds array... PASSED - Input accepted correctly
Testing Non-array kinds... PASSED - Invalid input properly rejected
Testing String in kinds... PASSED - Invalid input properly rejected
=== Timestamp Field Validation ===
Testing Valid since timestamp... PASSED - Input accepted correctly
Testing Valid until timestamp... PASSED - Input accepted correctly
Testing String since timestamp... PASSED - Invalid input properly rejected
Testing Negative timestamp... PASSED - Invalid input properly rejected
=== Limit Field Validation ===
Testing Valid limit... PASSED - Input accepted correctly
Testing Zero limit... PASSED - Input accepted correctly
Testing String limit... PASSED - Invalid input properly rejected
Testing Negative limit... PASSED - Invalid input properly rejected
=== Multiple Filters ===
Testing Two valid filters... PASSED - Input accepted correctly
Testing Many filters... PASSED - Input accepted correctly
=== Test Results ===
Total tests: 47
Passed: 47
Failed: 0
✓ All input validation tests passed!
The relay properly validates input.
2026-04-01 11:02:12 - \033[0;32m✓ Input Validation Tests PASSED\033[0m (Duration: 1s)
2026-04-01 11:02:12 -
2026-04-01 11:02:12 - \033[0;34m=== PERFORMANCE TEST SUITES ===\033[0m
2026-04-01 11:02:12 - ==========================================
2026-04-01 11:02:12 - Running Test Suite: Subscription Limit Tests
2026-04-01 11:02:12 - Description: Subscription limit enforcement testing
2026-04-01 11:02:12 - ==========================================
=== Subscription Limit Test ===
[INFO] Testing relay at: ws://127.0.0.1:8888
[INFO] Note: This test assumes default subscription limits (max 25 per client)
=== Test 1: Basic Connectivity ===
[INFO] Testing basic WebSocket connection...
[PASS] Basic connectivity works
=== Test 2: Subscription Limit Enforcement ===
[INFO] Testing subscription limits by creating multiple subscriptions...
[INFO] Creating multiple subscriptions within a single connection...
[INFO] Hit subscription limit at subscription 2
[PASS] Subscription limit enforcement working (limit hit after 1 subscriptions)
=== Test Complete ===
2026-04-01 11:02:12 - \033[0;32m✓ Subscription Limit Tests PASSED\033[0m (Duration: 0s)
2026-04-01 11:02:12 - ==========================================
2026-04-01 11:02:12 - Running Test Suite: Load Testing
2026-04-01 11:02:12 - Description: High concurrent connection testing
2026-04-01 11:02:12 - ==========================================
==========================================
C-Relay Load Testing Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
✓ Relay is accessible
==========================================
Load Test: Light Load Test
Description: Basic load test with moderate concurrent connections
Concurrent clients: 10
Messages per client: 5
==========================================
Launching 10 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 1s
Total connections attempted: 10
Successful connections: 10
Failed connections: 0
Connection success rate: 100%
Messages expected: 50
Messages sent: 50
Messages received: 100
✓ EXCELLENT: High connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Medium Load Test
Description: Moderate load test with higher concurrency
Concurrent clients: 25
Messages per client: 10
==========================================
Launching 25 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 4s
Total connections attempted: 35
Successful connections: 25
Failed connections: 0
Connection success rate: 71%
Messages expected: 250
Messages sent: 250
Messages received: 500
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Heavy Load Test
Description: Heavy load test with high concurrency
Concurrent clients: 50
Messages per client: 20
==========================================
Launching 50 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 14s
Total connections attempted: 85
Successful connections: 50
Failed connections: 0
Connection success rate: 58%
Messages expected: 1000
Messages sent: 1000
Messages received: 2000
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Stress Test
Description: Maximum load test to find breaking point
Concurrent clients: 100
Messages per client: 50
==========================================
Launching 100 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 64s
Total connections attempted: 185
Successful connections: 100
Failed connections: 0
Connection success rate: 54%
Messages expected: 5000
Messages sent: 5000
Messages received: 7500
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Testing Complete
==========================================
All load tests completed. Check individual test results above.
If any tests failed, the relay may need optimization or have resource limits.
2026-04-01 11:03:37 - \033[0;32m✓ Load Testing PASSED\033[0m (Duration: 85s)
2026-04-01 11:03:37 - ==========================================
2026-04-01 11:03:37 - Running Test Suite: Stress Testing
2026-04-01 11:03:37 - Description: Resource usage and stability testing
2026-04-01 11:03:37 - ==========================================
2026-04-01 11:03:37 - \033[0;31mERROR: Test script stress_tests.sh not found\033[0m
+733
View File
@@ -0,0 +1,733 @@
2026-04-01 11:29:24 - ==========================================
2026-04-01 11:29:24 - C-Relay Comprehensive Test Suite Runner
2026-04-01 11:29:24 - ==========================================
2026-04-01 11:29:24 - Relay URL: ws://127.0.0.1:8888
2026-04-01 11:29:24 - Log file: test_results_20260401_112924.log
2026-04-01 11:29:24 - Report file: test_report_20260401_112924.html
2026-04-01 11:29:24 -
2026-04-01 11:29:24 - Checking relay status at ws://127.0.0.1:8888...
2026-04-01 11:29:25 - \033[0;32m✓ Relay HTTP endpoint is accessible\033[0m
2026-04-01 11:29:25 -
2026-04-01 11:29:25 - Starting comprehensive test execution...
2026-04-01 11:29:25 -
2026-04-01 11:29:25 - \033[0;34m=== SECURITY TEST SUITES ===\033[0m
2026-04-01 11:29:25 - ==========================================
2026-04-01 11:29:25 - Running Test Suite: SQL Injection Tests
2026-04-01 11:29:25 - Description: Comprehensive SQL injection vulnerability testing
2026-04-01 11:29:25 - ==========================================
==========================================
C-Relay SQL Injection Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Valid query works
=== Authors Filter SQL Injection Tests ===
Testing Authors filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Authors filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== IDs Filter SQL Injection Tests ===
Testing IDs filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: #... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing IDs filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Kinds Filter SQL Injection Tests ===
Testing Kinds filter with string injection... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with negative value... PASSED - SQL injection blocked (rejected with error)
Testing Kinds filter with very large value... PASSED - SQL injection blocked (rejected with error)
=== Search Filter SQL Injection Tests ===
Testing Search filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: */... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing Search filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing Search filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Tag Filter SQL Injection Tests ===
Testing #e tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #e tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #p tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #t tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #r tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' OR 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: admin'--... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /*... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: */... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: /**/... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: #... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=1 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND 1=2 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (query sanitized)
Testing #d tag filter with payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (query sanitized)
=== Timestamp Filter SQL Injection Tests ===
Testing Since parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Until parameter injection... PASSED - SQL injection blocked (rejected with error)
=== Limit Parameter SQL Injection Tests ===
Testing Limit parameter injection... PASSED - SQL injection blocked (rejected with error)
Testing Limit with UNION... PASSED - SQL injection blocked (rejected with error)
=== Complex Multi-Filter SQL Injection Tests ===
Testing Multi-filter with authors injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with search injection... PASSED - SQL injection blocked (rejected with error)
Testing Multi-filter with tag injection... PASSED - SQL injection blocked (query sanitized)
=== COUNT Message SQL Injection Tests ===
Testing COUNT with authors payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' OR '1'='1... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' OR '1'='1... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1; SELECT * FROM sqlite_master; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' OR 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: admin'--... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' UNION SELECT password FROM users --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND SLEEP(5) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=1 WAITFOR DELAY '0:0:5' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CAST((SELECT version()) AS INT) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 1' AND 1=CONVERT(INT, (SELECT @@VERSION)) --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION SELECT 1,2,3 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' UNION ALL SELECT NULL,NULL,NULL --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; SELECT * FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DELETE FROM events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; UPDATE events SET content='hacked' WHERE 1=1; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /*... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: */... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: /**/... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: #... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: #... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: 0x53514C5F494E4A454354494F4E... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: J1NSTCBJTkpFQ1RJT04gLS0=... PASSED - SQL injection blocked (query sanitized)
Testing COUNT with authors payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '))); DROP TABLE events; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ')) UNION SELECT NULL; --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=1 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND 1=2 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: ' AND (SELECT COUNT(*) FROM events) > 0 --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; EXEC master..xp_cmdshell 'net user' --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with authors payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
Testing COUNT with search payload: '; DECLARE @host varchar(1024); SELECT @host=(SELECT TOP 1 master..sys.fn_varbintohexstr(password_hash) FROM sys.sql_logins WHERE name='sa'); --... PASSED - SQL injection blocked (rejected with error)
=== Edge Case SQL Injection Tests ===
Testing Empty string injection... PASSED - SQL injection blocked (rejected with error)
Testing Null byte injection... PASSED - SQL injection blocked (silently rejected)
Testing Unicode injection... PASSED - SQL injection blocked (rejected with error)
Testing Very long injection payload... PASSED - SQL injection blocked (rejected with error)
=== Subscription ID SQL Injection Tests ===
Testing Subscription ID injection... PASSED - SQL injection blocked (rejected with error)
Testing Subscription ID with quotes... PASSED - SQL injection blocked (silently rejected)
=== CLOSE Message SQL Injection Tests ===
Testing CLOSE with injection... PASSED - SQL injection blocked (rejected with error)
=== Test Results ===
Total tests: 318
Passed: 318
Failed: 0
✓ All SQL injection tests passed!
The relay appears to be protected against SQL injection attacks.
2026-04-01 11:29:32 - \033[0;32m✓ SQL Injection Tests PASSED\033[0m (Duration: 7s)
2026-04-01 11:29:32 - ==========================================
2026-04-01 11:29:32 - Running Test Suite: Filter Validation Tests
2026-04-01 11:29:32 - Description: Input validation for REQ and COUNT messages
2026-04-01 11:29:32 - ==========================================
=== C-Relay Filter Validation Tests ===
Testing against relay at ws://127.0.0.1:8888
Testing Valid REQ message... PASSED
Testing Valid COUNT message... PASSED
=== Testing Filter Array Validation ===
Testing Non-object filter... PASSED
Testing Too many filters... PASSED
=== Testing Authors Validation ===
Testing Invalid author type... PASSED
Testing Invalid author hex... PASSED
Testing Too many authors... PASSED
=== Testing IDs Validation ===
Testing Invalid ID type... PASSED
Testing Invalid ID hex... PASSED
Testing Too many IDs... PASSED
=== Testing Kinds Validation ===
Testing Invalid kind type... PASSED
Testing Negative kind... PASSED
Testing Too large kind... PASSED
Testing Too many kinds... PASSED
=== Testing Timestamp Validation ===
Testing Invalid since type... PASSED
Testing Negative since... PASSED
Testing Invalid until type... PASSED
Testing Negative until... PASSED
=== Testing Limit Validation ===
Testing Invalid limit type... PASSED
Testing Negative limit... PASSED
Testing Too large limit... PASSED
=== Testing Search Validation ===
Testing Invalid search type... PASSED
Testing Search too long... PASSED
Testing Search SQL injection... PASSED
=== Testing Tag Filter Validation ===
Testing Invalid tag filter type... PASSED
Testing Too many tag values... PASSED
Testing Tag value too long... PASSED
=== Testing Rate Limiting ===
Testing rate limiting with malformed requests... UNCERTAIN - Rate limiting may not have triggered (this could be normal)
=== Test Results ===
Total tests: 28
Passed: 28
Failed: 0
All tests passed!
2026-04-01 11:29:35 - \033[0;32m✓ Filter Validation Tests PASSED\033[0m (Duration: 3s)
2026-04-01 11:29:35 - ==========================================
2026-04-01 11:29:35 - Running Test Suite: Subscription Validation Tests
2026-04-01 11:29:35 - Description: Subscription ID and message validation
2026-04-01 11:29:35 - ==========================================
Testing subscription ID validation fixes...
Testing malformed subscription IDs...
Empty ID test: Connection failed (expected)
Long ID test: Connection failed (expected)
Invalid chars test: Connection failed (expected)
NULL ID test: Connection failed (expected)
Valid ID test: Failed
Testing CLOSE message validation...
CLOSE empty ID test: Connection failed (expected)
CLOSE valid ID test: Failed
Subscription validation tests completed.
2026-04-01 11:29:35 - \033[0;32m✓ Subscription Validation Tests PASSED\033[0m (Duration: 0s)
2026-04-01 11:29:35 - ==========================================
2026-04-01 11:29:35 - Running Test Suite: Memory Corruption Tests
2026-04-01 11:29:35 - Description: Buffer overflow and memory safety testing
2026-04-01 11:29:35 - ==========================================
==========================================
C-Relay Memory Corruption Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
Note: These tests may cause the relay to crash if vulnerabilities exist
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - No memory corruption detected
=== Subscription ID Memory Corruption Tests ===
Testing Empty subscription ID... UNCERTAIN - Expected error but got normal response
Testing Very long subscription ID (1KB)... UNCERTAIN - Expected error but got normal response
Testing Very long subscription ID (10KB)... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with null bytes... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with special chars... UNCERTAIN - Expected error but got normal response
Testing Unicode subscription ID... UNCERTAIN - Expected error but got normal response
Testing Subscription ID with path traversal... UNCERTAIN - Expected error but got normal response
=== Filter Array Memory Corruption Tests ===
Testing Too many filters (50)... UNCERTAIN - Expected error but got normal response
=== Concurrent Access Memory Tests ===
Testing Concurrent subscription creation... ["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
["EVENT","concurrent_1775057376377657551",{"pubkey":"7b013abdf0cbc8f994e66018539fbcb750f3be118a70d8bdcf3b3e86dd2ebb40","created_at":1775057288,"kind":1,"tags":[],"content":"\nRelay Statistics\n━━━━━━━━━━━━━━━━━━━━\nDatabase Size 0.00 MB (4096 bytes)\nTotal Events 0\nActive Subscriptions 0\nOldest Event -\nNewest Event -\n\nEvent_Kind_Distribution:\n\nTime-based Statistics:\n0\tEvents in the last day\n0\tEvents in the last week\n0\tEvents in the last month\n\n\n","id":"210f21884a3581064f4d181bc10e4088b5161216ddc7088f5c39e570240687b3","sig":"a04a697e6b3a53a1df4e3d7d06da2483f07d45c97a939076bd8b28216425a854af5f7e463542adbed9b71af68d36d7e0296be0048c3156a51ed0defa685a5171"}]
PASSED - Concurrent access handled safely
Testing Concurrent CLOSE operations...
PASSED - Concurrent access handled safely
=== Malformed JSON Memory Tests ===
Testing Unclosed JSON object... UNCERTAIN - Expected error but got normal response
Testing Mismatched brackets... UNCERTAIN - Expected error but got normal response
Testing Extra closing brackets... UNCERTAIN - Expected error but got normal response
Testing Null bytes in JSON... UNCERTAIN - Expected error but got normal response
=== Large Message Memory Tests ===
Testing Very large filter array... UNCERTAIN - Expected error but got normal response
Testing Very long search term... UNCERTAIN - Expected error but got normal response
=== Test Results ===
Total tests: 17
Passed: 17
Failed: 0
✓ All memory corruption tests passed!
The relay appears to handle memory safely.
2026-04-01 11:29:36 - \033[0;32m✓ Memory Corruption Tests PASSED\033[0m (Duration: 1s)
2026-04-01 11:29:36 - ==========================================
2026-04-01 11:29:36 - Running Test Suite: Input Validation Tests
2026-04-01 11:29:36 - Description: Comprehensive input boundary testing
2026-04-01 11:29:36 - ==========================================
==========================================
C-Relay Input Validation Test Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
Testing Basic connectivity... PASSED - Input accepted correctly
=== Message Type Validation ===
Testing Invalid message type - string... PASSED - Invalid input properly rejected
Testing Invalid message type - number... PASSED - Invalid input properly rejected
Testing Invalid message type - null... PASSED - Invalid input properly rejected
Testing Invalid message type - object... PASSED - Invalid input properly rejected
Testing Empty message type... PASSED - Invalid input properly rejected
Testing Very long message type... PASSED - Invalid input properly rejected
=== Message Structure Validation ===
Testing Too few arguments... PASSED - Invalid input properly rejected
Testing Too many arguments... PASSED - Invalid input properly rejected
Testing Non-array message... PASSED - Invalid input properly rejected
Testing Empty array... PASSED - Invalid input properly rejected
Testing Nested arrays incorrectly... PASSED - Invalid input properly rejected
=== Subscription ID Boundary Tests ===
Testing Valid subscription ID... PASSED - Input accepted correctly
Testing Empty subscription ID... PASSED - Invalid input properly rejected
Testing Subscription ID with spaces... PASSED - Invalid input properly rejected
Testing Subscription ID with newlines... PASSED - Invalid input properly rejected
Testing Subscription ID with tabs... PASSED - Invalid input properly rejected
Testing Subscription ID with control chars... PASSED - Invalid input properly rejected
Testing Unicode subscription ID... PASSED - Invalid input properly rejected
Testing Very long subscription ID... PASSED - Invalid input properly rejected
=== Filter Object Validation ===
Testing Valid empty filter... PASSED - Input accepted correctly
Testing Non-object filter... PASSED - Invalid input properly rejected
Testing Null filter... PASSED - Invalid input properly rejected
Testing Array filter... PASSED - Invalid input properly rejected
Testing Filter with invalid keys... PASSED - Input accepted correctly
=== Authors Field Validation ===
Testing Valid authors array... PASSED - Input accepted correctly
Testing Empty authors array... PASSED - Input accepted correctly
Testing Non-array authors... PASSED - Invalid input properly rejected
Testing Invalid hex in authors... PASSED - Invalid input properly rejected
Testing Short pubkey in authors... PASSED - Invalid input properly rejected
=== IDs Field Validation ===
Testing Valid ids array... PASSED - Input accepted correctly
Testing Empty ids array... PASSED - Input accepted correctly
Testing Non-array ids... PASSED - Invalid input properly rejected
=== Kinds Field Validation ===
Testing Valid kinds array... PASSED - Input accepted correctly
Testing Empty kinds array... PASSED - Input accepted correctly
Testing Non-array kinds... PASSED - Invalid input properly rejected
Testing String in kinds... PASSED - Invalid input properly rejected
=== Timestamp Field Validation ===
Testing Valid since timestamp... PASSED - Input accepted correctly
Testing Valid until timestamp... PASSED - Input accepted correctly
Testing String since timestamp... PASSED - Invalid input properly rejected
Testing Negative timestamp... PASSED - Invalid input properly rejected
=== Limit Field Validation ===
Testing Valid limit... PASSED - Input accepted correctly
Testing Zero limit... PASSED - Input accepted correctly
Testing String limit... PASSED - Invalid input properly rejected
Testing Negative limit... PASSED - Invalid input properly rejected
=== Multiple Filters ===
Testing Two valid filters... PASSED - Input accepted correctly
Testing Many filters... PASSED - Input accepted correctly
=== Test Results ===
Total tests: 47
Passed: 47
Failed: 0
✓ All input validation tests passed!
The relay properly validates input.
2026-04-01 11:29:38 - \033[0;32m✓ Input Validation Tests PASSED\033[0m (Duration: 2s)
2026-04-01 11:29:38 -
2026-04-01 11:29:38 - \033[0;34m=== PERFORMANCE TEST SUITES ===\033[0m
2026-04-01 11:29:38 - ==========================================
2026-04-01 11:29:38 - Running Test Suite: Subscription Limit Tests
2026-04-01 11:29:38 - Description: Subscription limit enforcement testing
2026-04-01 11:29:38 - ==========================================
=== Subscription Limit Test ===
[INFO] Testing relay at: ws://127.0.0.1:8888
[INFO] Note: This test assumes default subscription limits (max 25 per client)
=== Test 1: Basic Connectivity ===
[INFO] Testing basic WebSocket connection...
[PASS] Basic connectivity works
=== Test 2: Subscription Limit Enforcement ===
[INFO] Testing subscription limits by creating multiple subscriptions...
[INFO] Creating multiple subscriptions within a single connection...
[INFO] Hit subscription limit at subscription 2
[PASS] Subscription limit enforcement working (limit hit after 1 subscriptions)
=== Test Complete ===
2026-04-01 11:29:38 - \033[0;32m✓ Subscription Limit Tests PASSED\033[0m (Duration: 0s)
2026-04-01 11:29:38 - ==========================================
2026-04-01 11:29:38 - Running Test Suite: Load Testing
2026-04-01 11:29:38 - Description: High concurrent connection testing
2026-04-01 11:29:38 - ==========================================
==========================================
C-Relay Load Testing Suite
==========================================
Testing against relay at ws://127.0.0.1:8888
=== Basic Connectivity Test ===
✓ Relay is accessible
==========================================
Load Test: Light Load Test
Description: Basic load test with moderate concurrent connections
Concurrent clients: 10
Messages per client: 5
==========================================
Launching 10 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 1s
Total connections attempted: 10
Successful connections: 10
Failed connections: 0
Connection success rate: 100%
Messages expected: 50
Messages sent: 50
Messages received: 100
✓ EXCELLENT: High connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Medium Load Test
Description: Moderate load test with higher concurrency
Concurrent clients: 25
Messages per client: 10
==========================================
Launching 25 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 4s
Total connections attempted: 35
Successful connections: 25
Failed connections: 0
Connection success rate: 71%
Messages expected: 250
Messages sent: 250
Messages received: 500
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Heavy Load Test
Description: Heavy load test with high concurrency
Concurrent clients: 50
Messages per client: 20
==========================================
Launching 50 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 14s
Total connections attempted: 85
Successful connections: 50
Failed connections: 0
Connection success rate: 58%
Messages expected: 1000
Messages sent: 1000
Messages received: 2000
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Test: Stress Test
Description: Maximum load test to find breaking point
Concurrent clients: 100
Messages per client: 50
==========================================
Launching 100 clients...
All clients completed. Processing results...
=== Load Test Results ===
Test duration: 64s
Total connections attempted: 185
Successful connections: 100
Failed connections: 0
Connection success rate: 54%
Messages expected: 5000
Messages sent: 5000
Messages received: 7500
✗ POOR: Low connection success rate
Checking relay responsiveness... ✓ Relay is still responsive
==========================================
Load Testing Complete
==========================================
All load tests completed. Check individual test results above.
If any tests failed, the relay may need optimization or have resource limits.
2026-04-01 11:31:03 - \033[0;32m✓ Load Testing PASSED\033[0m (Duration: 85s)
2026-04-01 11:31:03 - ==========================================
2026-04-01 11:31:03 - Running Test Suite: Stress Testing
2026-04-01 11:31:03 - Description: Resource usage and stability testing
2026-04-01 11:31:03 - ==========================================
2026-04-01 11:31:03 - \033[0;31mERROR: Test script stress_tests.sh not found\033[0m
+183
View File
@@ -0,0 +1,183 @@
#!/bin/bash
# Thread-level CPU profiler for c-relay on remote server
# - Samples per-thread CPU every N seconds using /proc/<pid>/task/*/stat deltas
# - Optionally runs perf record in parallel for callgraph data
# - Pulls artifacts locally and generates a summary report
set -euo pipefail
REMOTE_HOST="${REMOTE_HOST:-ubuntu@laantungir.com}"
DURATION="${DURATION:-600}" # seconds (default 10 minutes)
INTERVAL="${INTERVAL:-10}" # seconds between samples
OUTDIR_BASE="${OUTDIR_BASE:-thread_profile_runs}"
RUN_TS="$(date -u +%Y%m%d_%H%M%S)"
RUN_DIR="${OUTDIR_BASE}/${RUN_TS}"
LOCAL_CSV="${RUN_DIR}/thread_samples.csv"
LOCAL_REPORT="${RUN_DIR}/thread_summary.txt"
REMOTE_DIR="/tmp/c_relay_thread_profile_${RUN_TS}"
ENABLE_PERF="${ENABLE_PERF:-1}" # 1=run perf, 0=skip
mkdir -p "${RUN_DIR}"
echo "=========================================="
echo "C-Relay Thread CPU Profiler"
echo "=========================================="
echo "Remote host : ${REMOTE_HOST}"
echo "Duration : ${DURATION}s"
echo "Interval : ${INTERVAL}s"
echo "Run dir : ${RUN_DIR}"
echo "Perf record : ${ENABLE_PERF}"
echo ""
ssh "${REMOTE_HOST}" "bash -s" <<EOF
set -euo pipefail
REMOTE_DIR="${REMOTE_DIR}"
DURATION="${DURATION}"
INTERVAL="${INTERVAL}"
ENABLE_PERF="${ENABLE_PERF}"
mkdir -p "\${REMOTE_DIR}"
CSV_FILE="\${REMOTE_DIR}/thread_samples.csv"
PERF_FILE="\${REMOTE_DIR}/perf.data"
LOG_FILE="\${REMOTE_DIR}/run.log"
PID=\$(pgrep -f '/usr/local/bin/c_relay/c_relay|c_relay' | head -1 || true)
if [ -z "\${PID}" ]; then
echo "ERROR: c_relay process not found (checked by cmdline pattern)" | tee -a "\${LOG_FILE}"
exit 1
fi
echo "Profiling PID: \${PID}" | tee -a "\${LOG_FILE}"
CLK_TCK=\$(getconf CLK_TCK)
echo "CLK_TCK=\${CLK_TCK}" | tee -a "\${LOG_FILE}"
echo "timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks" > "\${CSV_FILE}"
# Start perf in background (if enabled and available)
PERF_PID=""
if [ "\${ENABLE_PERF}" = "1" ]; then
if command -v perf >/dev/null 2>&1; then
if sudo -n true >/dev/null 2>&1; then
echo "Starting perf record in background..." | tee -a "\${LOG_FILE}"
sudo perf record -g -p "\${PID}" -o "\${PERF_FILE}" -- sleep "\${DURATION}" >/dev/null 2>&1 &
PERF_PID=\$!
PERF_OWNER="\$(id -un)"
echo "perf_pid=\${PERF_PID}" | tee -a "\${LOG_FILE}"
else
echo "Skipping perf: sudo -n not available" | tee -a "\${LOG_FILE}"
fi
else
echo "Skipping perf: command not found" | tee -a "\${LOG_FILE}"
fi
fi
# Capture previous totals by tid
declare -A PREV_TOTAL
capture_totals() {
local pid="\$1"
for statf in /proc/\${pid}/task/*/stat; do
[ -f "\${statf}" ] || continue
local tid
tid=\${statf%/stat}
tid=\${tid##*/}
local total
total=\$(awk '{print \$14+\$15}' "\${statf}" 2>/dev/null || echo 0)
PREV_TOTAL[\${tid}]="\${total}"
done
}
capture_totals "\${PID}"
START_TS=\$(date +%s)
END_TS=\$((START_TS + DURATION))
while [ \$(date +%s) -lt \${END_TS} ]; do
NOW=\$(date +%s)
for statf in /proc/\${PID}/task/*/stat; do
[ -f "\${statf}" ] || continue
tid=\${statf%/stat}
tid=\${tid##*/}
total=\$(awk '{print \$14+\$15}' "\${statf}" 2>/dev/null || echo 0)
prev=\${PREV_TOTAL[\${tid}]:-\${total}}
delta=\$((total - prev))
if [ \${delta} -lt 0 ]; then
delta=0
fi
PREV_TOTAL[\${tid}]="\${total}"
name_file="/proc/\${PID}/task/\${tid}/comm"
if [ -f "\${name_file}" ]; then
tname=\$(tr -d '\n' < "\${name_file}")
else
tname="unknown"
fi
cpu_pct=\$(awk -v d="\${delta}" -v hz="\${CLK_TCK}" -v iv="\${INTERVAL}" 'BEGIN { printf "%.2f", (d / hz) * 100.0 / iv }')
echo "\${NOW},\${tid},\${tname},\${cpu_pct},\${delta},\${total}" >> "\${CSV_FILE}"
done
sleep "\${INTERVAL}"
done
if [ -n "\${PERF_PID}" ]; then
wait "\${PERF_PID}" || true
if [ -f "\${PERF_FILE}" ]; then
sudo perf report --stdio -i "\${PERF_FILE}" --sort=comm,symbol --no-children -n > "\${REMOTE_DIR}/perf_report.txt" 2>/dev/null || true
sudo chown "\${PERF_OWNER}:\${PERF_OWNER}" "\${PERF_FILE}" "\${REMOTE_DIR}/perf_report.txt" 2>/dev/null || true
fi
fi
echo "Done. Artifacts in \${REMOTE_DIR}" | tee -a "\${LOG_FILE}"
EOF
echo "Fetching artifacts..."
scp -q "${REMOTE_HOST}:${REMOTE_DIR}/thread_samples.csv" "${LOCAL_CSV}"
scp -q "${REMOTE_HOST}:${REMOTE_DIR}/run.log" "${RUN_DIR}/run.log" || true
scp -q "${REMOTE_HOST}:${REMOTE_DIR}/perf.data" "${RUN_DIR}/perf.data" || true
scp -q "${REMOTE_HOST}:${REMOTE_DIR}/perf_report.txt" "${RUN_DIR}/perf_report.txt" || true
echo "Generating summary..."
{
echo "=========================================="
echo "Thread CPU Summary (avg + max over run)"
echo "=========================================="
echo "Run timestamp (UTC): ${RUN_TS}"
echo "Duration: ${DURATION}s, Interval: ${INTERVAL}s"
echo ""
awk -F, '
NR==1 {next}
{
key=$2"|"$3;
cpu=$4+0;
sum[key]+=cpu;
cnt[key]++;
if (cpu > max[key]) max[key]=cpu;
}
END {
printf("%-10s %-18s %12s %12s\n", "TID", "THREAD", "AVG_CPU%", "MAX_CPU%");
for (k in sum) {
split(k, a, "|");
avg=sum[k]/cnt[k];
printf("%-10s %-18s %12.2f %12.2f\n", a[1], a[2], avg, max[k]);
}
}
' "${LOCAL_CSV}" | sort -k3,3nr
} | tee "${LOCAL_REPORT}"
echo ""
echo "Completed. Files:"
echo " ${LOCAL_CSV}"
echo " ${LOCAL_REPORT}"
if [ -f "${RUN_DIR}/perf_report.txt" ]; then
echo " ${RUN_DIR}/perf_report.txt"
fi
echo ""
echo "Top 20 summary:"
head -n 22 "${LOCAL_REPORT}"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
Profiling PID: 2040140
CLK_TCK=100
Starting perf record in background...
perf_pid=2040808
Done. Artifacts in /tmp/c_relay_thread_profile_20260401_154500
@@ -0,0 +1,421 @@
timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks
1775058301,2040140",unknown,0.00,0,3950
1775058301,2040150",unknown,0.00,0,9
1775058301,2040151",unknown,0.00,0,8
1775058301,2040152",unknown,0.00,0,11
1775058301,2040153",unknown,0.00,0,11
1775058301,2040154",unknown,0.00,0,0
1775058301,2040155",unknown,0.00,0,0
1775058311,2040140",unknown,62.10,621,4571
1775058311,2040150",unknown,0.20,2,11
1775058311,2040151",unknown,0.10,1,9
1775058311,2040152",unknown,0.10,1,12
1775058311,2040153",unknown,0.00,0,11
1775058311,2040154",unknown,0.00,0,0
1775058311,2040155",unknown,0.00,0,0
1775058321,2040140",unknown,65.20,652,5223
1775058321,2040150",unknown,0.20,2,13
1775058321,2040151",unknown,0.20,2,11
1775058321,2040152",unknown,0.30,3,15
1775058321,2040153",unknown,0.20,2,13
1775058321,2040154",unknown,0.00,0,0
1775058321,2040155",unknown,0.00,0,0
1775058331,2040140",unknown,83.80,838,6061
1775058331,2040150",unknown,0.00,0,13
1775058331,2040151",unknown,0.00,0,11
1775058331,2040152",unknown,0.00,0,15
1775058331,2040153",unknown,0.00,0,13
1775058331,2040154",unknown,0.00,0,0
1775058331,2040155",unknown,0.00,0,0
1775058341,2040140",unknown,100.20,1002,7063
1775058341,2040150",unknown,0.00,0,13
1775058341,2040151",unknown,0.00,0,11
1775058341,2040152",unknown,0.00,0,15
1775058341,2040153",unknown,0.00,0,13
1775058341,2040154",unknown,0.00,0,0
1775058341,2040155",unknown,0.00,0,0
1775058351,2040140",unknown,100.30,1003,8066
1775058351,2040150",unknown,0.00,0,13
1775058351,2040151",unknown,0.00,0,11
1775058351,2040152",unknown,0.10,1,16
1775058351,2040153",unknown,0.00,0,13
1775058351,2040154",unknown,0.00,0,0
1775058351,2040155",unknown,0.00,0,0
1775058361,2040140",unknown,89.50,895,8961
1775058361,2040150",unknown,0.10,1,14
1775058361,2040151",unknown,0.00,0,11
1775058361,2040152",unknown,0.00,0,16
1775058361,2040153",unknown,0.00,0,13
1775058361,2040154",unknown,0.00,0,0
1775058361,2040155",unknown,0.00,0,0
1775058371,2040140",unknown,21.20,212,9173
1775058371,2040150",unknown,0.00,0,14
1775058371,2040151",unknown,0.00,0,11
1775058371,2040152",unknown,0.00,0,16
1775058371,2040153",unknown,0.00,0,13
1775058371,2040154",unknown,0.00,0,0
1775058371,2040155",unknown,0.00,0,0
1775058381,2040140",unknown,36.40,364,9537
1775058381,2040150",unknown,0.10,1,15
1775058381,2040151",unknown,0.10,1,12
1775058381,2040152",unknown,0.10,1,17
1775058381,2040153",unknown,0.00,0,13
1775058381,2040154",unknown,0.00,0,0
1775058381,2040155",unknown,0.00,0,0
1775058391,2040140",unknown,35.30,353,9890
1775058391,2040150",unknown,0.00,0,15
1775058391,2040151",unknown,0.00,0,12
1775058391,2040152",unknown,0.00,0,17
1775058391,2040153",unknown,0.00,0,13
1775058391,2040154",unknown,0.00,0,0
1775058391,2040155",unknown,0.00,0,0
1775058401,2040140",unknown,42.80,428,10318
1775058401,2040150",unknown,0.30,3,18
1775058401,2040151",unknown,0.20,2,14
1775058401,2040152",unknown,0.30,3,20
1775058401,2040153",unknown,0.30,3,16
1775058401,2040154",unknown,0.00,0,0
1775058401,2040155",unknown,0.00,0,0
1775058411,2040140",unknown,73.50,735,11053
1775058411,2040150",unknown,0.40,4,22
1775058411,2040151",unknown,0.20,2,16
1775058411,2040152",unknown,0.40,4,24
1775058411,2040153",unknown,0.20,2,18
1775058411,2040154",unknown,0.00,0,0
1775058411,2040155",unknown,0.00,0,0
1775058421,2040140",unknown,33.60,336,11389
1775058421,2040150",unknown,0.00,0,22
1775058421,2040151",unknown,0.00,0,16
1775058421,2040152",unknown,0.00,0,24
1775058421,2040153",unknown,0.10,1,19
1775058421,2040154",unknown,0.00,0,0
1775058421,2040155",unknown,0.00,0,0
1775058431,2040140",unknown,57.20,572,11961
1775058431,2040150",unknown,0.00,0,22
1775058431,2040151",unknown,0.10,1,17
1775058431,2040152",unknown,0.10,1,25
1775058431,2040153",unknown,0.00,0,19
1775058431,2040154",unknown,0.00,0,0
1775058431,2040155",unknown,0.00,0,0
1775058441,2040140",unknown,96.70,967,12928
1775058441,2040150",unknown,0.00,0,22
1775058441,2040151",unknown,0.00,0,17
1775058441,2040152",unknown,0.00,0,25
1775058441,2040153",unknown,0.00,0,19
1775058441,2040154",unknown,0.00,0,0
1775058441,2040155",unknown,0.00,0,0
1775058451,2040140",unknown,100.30,1003,13931
1775058451,2040150",unknown,0.10,1,23
1775058451,2040151",unknown,0.10,1,18
1775058451,2040152",unknown,0.10,1,26
1775058451,2040153",unknown,0.00,0,19
1775058451,2040154",unknown,0.00,0,0
1775058451,2040155",unknown,0.00,0,0
1775058461,2040140",unknown,98.40,984,14915
1775058461,2040150",unknown,0.10,1,24
1775058461,2040151",unknown,0.30,3,21
1775058461,2040152",unknown,0.10,1,27
1775058461,2040153",unknown,0.20,2,21
1775058461,2040154",unknown,0.00,0,0
1775058461,2040155",unknown,0.00,0,0
1775058471,2040140",unknown,62.90,629,15544
1775058471,2040150",unknown,0.00,0,24
1775058471,2040151",unknown,0.20,2,23
1775058471,2040152",unknown,0.00,0,27
1775058471,2040153",unknown,0.10,1,22
1775058471,2040154",unknown,0.00,0,0
1775058471,2040155",unknown,0.20,2,2
1775058481,2040140",unknown,83.20,832,16376
1775058481,2040150",unknown,0.20,2,26
1775058481,2040151",unknown,0.00,0,23
1775058481,2040152",unknown,0.20,2,29
1775058481,2040153",unknown,0.00,0,22
1775058481,2040154",unknown,0.00,0,0
1775058481,2040155",unknown,0.00,0,2
1775058491,2040140",unknown,100.30,1003,17379
1775058491,2040150",unknown,0.00,0,26
1775058491,2040151",unknown,0.00,0,23
1775058491,2040152",unknown,0.00,0,29
1775058491,2040153",unknown,0.00,0,22
1775058491,2040154",unknown,0.00,0,0
1775058491,2040155",unknown,0.00,0,2
1775058501,2040140",unknown,63.30,633,18012
1775058501,2040150",unknown,0.10,1,27
1775058501,2040151",unknown,0.10,1,24
1775058501,2040152",unknown,0.30,3,32
1775058501,2040153",unknown,0.20,2,24
1775058501,2040154",unknown,0.00,0,0
1775058501,2040155",unknown,0.00,0,2
1775058511,2040140",unknown,57.40,574,18586
1775058511,2040150",unknown,0.00,0,27
1775058511,2040151",unknown,0.10,1,25
1775058511,2040152",unknown,0.00,0,32
1775058511,2040153",unknown,0.00,0,24
1775058511,2040154",unknown,0.00,0,0
1775058511,2040155",unknown,0.00,0,2
1775058521,2040140",unknown,18.70,187,18773
1775058521,2040150",unknown,0.10,1,28
1775058521,2040151",unknown,0.00,0,25
1775058521,2040152",unknown,0.00,0,32
1775058521,2040153",unknown,0.00,0,24
1775058521,2040154",unknown,0.00,0,0
1775058521,2040155",unknown,0.00,0,2
1775058531,2040140",unknown,66.20,662,19435
1775058531,2040150",unknown,0.00,0,28
1775058531,2040151",unknown,0.10,1,26
1775058531,2040152",unknown,0.00,0,32
1775058531,2040153",unknown,0.00,0,24
1775058531,2040154",unknown,0.00,0,0
1775058531,2040155",unknown,0.00,0,2
1775058541,2040140",unknown,89.10,891,20326
1775058541,2040150",unknown,0.00,0,28
1775058541,2040151",unknown,0.00,0,26
1775058541,2040152",unknown,0.10,1,33
1775058541,2040153",unknown,0.00,0,24
1775058541,2040154",unknown,0.00,0,0
1775058541,2040155",unknown,0.00,0,2
1775058551,2040140",unknown,38.10,381,20707
1775058551,2040150",unknown,0.10,1,29
1775058551,2040151",unknown,0.00,0,26
1775058551,2040152",unknown,0.00,0,33
1775058551,2040153",unknown,0.00,0,24
1775058551,2040154",unknown,0.00,0,0
1775058551,2040155",unknown,0.00,0,2
1775058562,2040140",unknown,69.20,692,21399
1775058562,2040150",unknown,0.00,0,29
1775058562,2040151",unknown,0.20,2,28
1775058562,2040152",unknown,0.00,0,33
1775058562,2040153",unknown,0.10,1,25
1775058562,2040154",unknown,0.00,0,0
1775058562,2040155",unknown,0.00,0,2
1775058572,2040140",unknown,92.80,928,22327
1775058572,2040150",unknown,0.00,0,29
1775058572,2040151",unknown,0.00,0,28
1775058572,2040152",unknown,0.00,0,33
1775058572,2040153",unknown,0.00,0,25
1775058572,2040154",unknown,0.00,0,0
1775058572,2040155",unknown,0.00,0,2
1775058582,2040140",unknown,91.30,913,23240
1775058582,2040150",unknown,0.10,1,30
1775058582,2040151",unknown,0.00,0,28
1775058582,2040152",unknown,0.10,1,34
1775058582,2040153",unknown,0.00,0,25
1775058582,2040154",unknown,0.00,0,0
1775058582,2040155",unknown,0.00,0,2
1775058592,2040140",unknown,94.60,946,24186
1775058592,2040150",unknown,0.00,0,30
1775058592,2040151",unknown,0.00,0,28
1775058592,2040152",unknown,0.10,1,35
1775058592,2040153",unknown,0.10,1,26
1775058592,2040154",unknown,0.00,0,0
1775058592,2040155",unknown,0.00,0,2
1775058602,2040140",unknown,45.50,455,24641
1775058602,2040150",unknown,0.00,0,30
1775058602,2040151",unknown,0.00,0,28
1775058602,2040152",unknown,0.00,0,35
1775058602,2040153",unknown,0.00,0,26
1775058602,2040154",unknown,0.00,0,0
1775058602,2040155",unknown,0.00,0,2
1775058612,2040140",unknown,64.50,645,25286
1775058612,2040150",unknown,0.00,0,30
1775058612,2040151",unknown,0.10,1,29
1775058612,2040152",unknown,0.00,0,35
1775058612,2040153",unknown,0.10,1,27
1775058612,2040154",unknown,0.00,0,0
1775058612,2040155",unknown,0.00,0,2
1775058622,2040140",unknown,21.10,211,25497
1775058622,2040150",unknown,0.00,0,30
1775058622,2040151",unknown,0.00,0,29
1775058622,2040152",unknown,0.00,0,35
1775058622,2040153",unknown,0.00,0,27
1775058622,2040154",unknown,0.00,0,0
1775058622,2040155",unknown,0.00,0,2
1775058632,2040140",unknown,26.60,266,25763
1775058632,2040150",unknown,0.20,2,32
1775058632,2040151",unknown,0.10,1,30
1775058632,2040152",unknown,0.00,0,35
1775058632,2040153",unknown,0.00,0,27
1775058632,2040154",unknown,0.10,1,1
1775058632,2040155",unknown,0.00,0,2
1775058642,2040140",unknown,51.80,518,26281
1775058642,2040150",unknown,0.00,0,32
1775058642,2040151",unknown,0.10,1,31
1775058642,2040152",unknown,0.00,0,35
1775058642,2040153",unknown,0.00,0,27
1775058642,2040154",unknown,0.00,0,1
1775058642,2040155",unknown,0.00,0,2
1775058652,2040140",unknown,28.40,284,26565
1775058652,2040150",unknown,0.00,0,32
1775058652,2040151",unknown,0.10,1,32
1775058652,2040152",unknown,0.10,1,36
1775058652,2040153",unknown,0.20,2,29
1775058652,2040154",unknown,0.00,0,1
1775058652,2040155",unknown,0.00,0,2
1775058662,2040140",unknown,14.30,143,26708
1775058662,2040150",unknown,0.20,2,34
1775058662,2040151",unknown,0.10,1,33
1775058662,2040152",unknown,0.40,4,40
1775058662,2040153",unknown,0.30,3,32
1775058662,2040154",unknown,0.00,0,1
1775058662,2040155",unknown,0.00,0,2
1775058672,2040140",unknown,69.20,692,27400
1775058672,2040150",unknown,0.20,2,36
1775058672,2040151",unknown,0.00,0,33
1775058672,2040152",unknown,0.20,2,42
1775058672,2040153",unknown,0.10,1,33
1775058672,2040154",unknown,0.00,0,1
1775058672,2040155",unknown,0.00,0,2
1775058682,2040140",unknown,71.10,711,28111
1775058682,2040150",unknown,0.30,3,39
1775058682,2040151",unknown,0.20,2,35
1775058682,2040152",unknown,0.00,0,42
1775058682,2040153",unknown,0.10,1,34
1775058682,2040154",unknown,0.00,0,1
1775058682,2040155",unknown,0.00,0,2
1775058692,2040140",unknown,99.40,994,29105
1775058692,2040150",unknown,0.00,0,39
1775058692,2040151",unknown,0.00,0,35
1775058692,2040152",unknown,0.00,0,42
1775058692,2040153",unknown,0.00,0,34
1775058692,2040154",unknown,0.00,0,1
1775058692,2040155",unknown,0.00,0,2
1775058702,2040140",unknown,30.60,306,29411
1775058702,2040150",unknown,0.10,1,40
1775058702,2040151",unknown,0.10,1,36
1775058702,2040152",unknown,0.20,2,44
1775058702,2040153",unknown,0.20,2,36
1775058702,2040154",unknown,0.00,0,1
1775058702,2040155",unknown,0.00,0,2
1775058712,2040140",unknown,14.70,147,29558
1775058712,2040150",unknown,0.00,0,40
1775058712,2040151",unknown,0.00,0,36
1775058712,2040152",unknown,0.10,1,45
1775058712,2040153",unknown,0.00,0,36
1775058712,2040154",unknown,0.10,1,2
1775058712,2040155",unknown,0.00,0,2
1775058722,2040140",unknown,14.20,142,29700
1775058722,2040150",unknown,0.00,0,40
1775058722,2040151",unknown,0.10,1,37
1775058722,2040152",unknown,0.10,1,46
1775058722,2040153",unknown,0.00,0,36
1775058722,2040154",unknown,0.00,0,2
1775058722,2040155",unknown,0.00,0,2
1775058732,2040140",unknown,29.20,292,29992
1775058732,2040150",unknown,0.10,1,41
1775058732,2040151",unknown,0.00,0,37
1775058732,2040152",unknown,0.00,0,46
1775058732,2040153",unknown,0.00,0,36
1775058732,2040154",unknown,0.00,0,2
1775058732,2040155",unknown,0.00,0,2
1775058742,2040140",unknown,29.80,298,30290
1775058742,2040150",unknown,0.00,0,41
1775058742,2040151",unknown,0.00,0,37
1775058742,2040152",unknown,0.10,1,47
1775058742,2040153",unknown,0.00,0,36
1775058742,2040154",unknown,0.00,0,2
1775058742,2040155",unknown,0.00,0,2
1775058752,2040140",unknown,48.20,482,30772
1775058752,2040150",unknown,0.00,0,41
1775058752,2040151",unknown,0.00,0,37
1775058752,2040152",unknown,0.00,0,47
1775058752,2040153",unknown,0.10,1,37
1775058752,2040154",unknown,0.00,0,2
1775058752,2040155",unknown,0.00,0,2
1775058762,2040140",unknown,28.10,281,31053
1775058762,2040150",unknown,0.00,0,41
1775058762,2040151",unknown,0.00,0,37
1775058762,2040152",unknown,0.00,0,47
1775058762,2040153",unknown,0.00,0,37
1775058762,2040154",unknown,0.00,0,2
1775058762,2040155",unknown,0.00,0,2
1775058772,2040140",unknown,40.80,408,31461
1775058772,2040150",unknown,0.10,1,42
1775058772,2040151",unknown,0.10,1,38
1775058772,2040152",unknown,0.10,1,48
1775058772,2040153",unknown,0.10,1,38
1775058772,2040154",unknown,0.00,0,2
1775058772,2040155",unknown,0.00,0,2
1775058782,2040140",unknown,98.90,989,32450
1775058782,2040150",unknown,0.30,3,45
1775058782,2040151",unknown,0.00,0,38
1775058782,2040152",unknown,0.30,3,51
1775058782,2040153",unknown,0.10,1,39
1775058782,2040154",unknown,0.00,0,2
1775058782,2040155",unknown,0.10,1,3
1775058792,2040140",unknown,59.50,595,33045
1775058792,2040150",unknown,0.20,2,47
1775058792,2040151",unknown,0.10,1,39
1775058792,2040152",unknown,0.10,1,52
1775058792,2040153",unknown,0.10,1,40
1775058792,2040154",unknown,0.00,0,2
1775058792,2040155",unknown,0.00,0,3
1775058802,2040140",unknown,47.10,471,33516
1775058802,2040150",unknown,0.00,0,47
1775058802,2040151",unknown,0.00,0,39
1775058802,2040152",unknown,0.00,0,52
1775058802,2040153",unknown,0.10,1,41
1775058802,2040154",unknown,0.00,0,2
1775058802,2040155",unknown,0.10,1,4
1775058812,2040140",unknown,50.60,506,34022
1775058812,2040150",unknown,0.00,0,47
1775058812,2040151",unknown,0.00,0,39
1775058812,2040152",unknown,0.10,1,53
1775058812,2040153",unknown,0.00,0,41
1775058812,2040154",unknown,0.00,0,2
1775058812,2040155",unknown,0.10,1,5
1775058822,2040140",unknown,28.10,281,34303
1775058822,2040150",unknown,0.00,0,47
1775058822,2040151",unknown,0.00,0,39
1775058822,2040152",unknown,0.00,0,53
1775058822,2040153",unknown,0.00,0,41
1775058822,2040154",unknown,0.00,0,2
1775058822,2040155",unknown,0.00,0,5
1775058832,2040140",unknown,75.30,753,35056
1775058832,2040150",unknown,0.10,1,48
1775058832,2040151",unknown,0.10,1,40
1775058832,2040152",unknown,0.10,1,54
1775058832,2040153",unknown,0.10,1,42
1775058832,2040154",unknown,0.00,0,2
1775058832,2040155",unknown,0.00,0,5
1775058842,2040140",unknown,37.20,372,35428
1775058842,2040150",unknown,0.00,0,48
1775058842,2040151",unknown,0.00,0,40
1775058842,2040152",unknown,0.00,0,54
1775058842,2040153",unknown,0.00,0,42
1775058842,2040154",unknown,0.00,0,2
1775058842,2040155",unknown,0.00,0,5
1775058852,2040140",unknown,27.00,270,35698
1775058852,2040150",unknown,0.20,2,50
1775058852,2040151",unknown,0.20,2,42
1775058852,2040152",unknown,0.30,3,57
1775058852,2040153",unknown,0.20,2,44
1775058852,2040154",unknown,0.00,0,2
1775058852,2040155",unknown,0.00,0,5
1775058862,2040140",unknown,46.30,463,36161
1775058862,2040150",unknown,0.00,0,50
1775058862,2040151",unknown,0.10,1,43
1775058862,2040152",unknown,0.20,2,59
1775058862,2040153",unknown,0.00,0,44
1775058862,2040154",unknown,0.00,0,2
1775058862,2040155",unknown,0.00,0,5
1775058872,2040140",unknown,14.00,140,36301
1775058872,2040150",unknown,0.00,0,50
1775058872,2040151",unknown,0.00,0,43
1775058872,2040152",unknown,0.00,0,59
1775058872,2040153",unknown,0.00,0,44
1775058872,2040154",unknown,0.00,0,2
1775058872,2040155",unknown,0.00,0,5
1775058882,2040140",unknown,56.20,562,36863
1775058882,2040150",unknown,0.00,0,50
1775058882,2040151",unknown,0.10,1,44
1775058882,2040152",unknown,0.10,1,60
1775058882,2040153",unknown,0.00,0,44
1775058882,2040154",unknown,0.00,0,2
1775058882,2040155",unknown,0.00,0,5
1775058893,2040140",unknown,35.60,356,37219
1775058893,2040150",unknown,0.20,2,52
1775058893,2040151",unknown,0.10,1,45
1775058893,2040152",unknown,0.10,1,61
1775058893,2040153",unknown,0.10,1,45
1775058893,2040154",unknown,0.00,0,2
1775058893,2040155",unknown,0.00,0,5
Can't render this file because it contains an unexpected character in line 2 and column 19.
@@ -0,0 +1,14 @@
==========================================
Thread CPU Summary (avg + max over run)
==========================================
Run timestamp (UTC): 20260401_154500
Duration: 600s, Interval: 10s
2040140" unknown 55.45 100.30
2040152" unknown 0.08 0.40
2040150" unknown 0.07 0.40
2040151" unknown 0.06 0.30
2040153" unknown 0.06 0.30
2040155" unknown 0.01 0.20
2040154" unknown 0.00 0.10
TID THREAD AVG_CPU% MAX_CPU%
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
Profiling PID: 2040140
CLK_TCK=100
Starting perf record in background...
perf_pid=2046339
Done. Artifacts in /tmp/c_relay_thread_profile_20260401_155603
@@ -0,0 +1,372 @@
timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks
1775058964,2040140,lws-main,0.00,0,42849
1775058964,2040150,db-read-1,0.00,0,58
1775058964,2040151,db-read-2,0.00,0,54
1775058964,2040152,db-read-3,0.10,1,68
1775058964,2040153,db-read-4,0.00,0,53
1775058964,2040154,db-write,0.00,0,2
1775058964,2040155,event-worker,0.00,0,5
1775058974,2040140,lws-main,59.30,593,43442
1775058974,2040150,db-read-1,0.00,0,58
1775058974,2040151,db-read-2,0.00,0,54
1775058974,2040152,db-read-3,0.10,1,69
1775058974,2040153,db-read-4,0.00,0,53
1775058974,2040154,db-write,0.00,0,2
1775058974,2040155,event-worker,0.00,0,5
1775058984,2040140,lws-main,67.40,674,44116
1775058984,2040150,db-read-1,0.00,0,58
1775058984,2040151,db-read-2,0.00,0,54
1775058984,2040152,db-read-3,0.00,0,69
1775058984,2040153,db-read-4,0.10,1,54
1775058984,2040154,db-write,0.00,0,2
1775058984,2040155,event-worker,0.00,0,5
1775058994,2040140,lws-main,98.70,987,45103
1775058994,2040150,db-read-1,0.10,1,59
1775058994,2040151,db-read-2,0.00,0,54
1775058994,2040152,db-read-3,0.00,0,69
1775058994,2040153,db-read-4,0.00,0,54
1775058994,2040154,db-write,0.00,0,2
1775058994,2040155,event-worker,0.00,0,5
1775059004,2040140,lws-main,71.00,710,45813
1775059004,2040150,db-read-1,0.00,0,59
1775059004,2040151,db-read-2,0.00,0,54
1775059004,2040152,db-read-3,0.10,1,70
1775059004,2040153,db-read-4,0.10,1,55
1775059004,2040154,db-write,0.00,0,2
1775059004,2040155,event-worker,0.00,0,5
1775059014,2040140,lws-main,14.70,147,45960
1775059014,2040150,db-read-1,0.10,1,60
1775059014,2040151,db-read-2,0.30,3,57
1775059014,2040152,db-read-3,0.10,1,71
1775059014,2040153,db-read-4,0.10,1,56
1775059014,2040154,db-write,0.00,0,2
1775059014,2040155,event-worker,0.00,0,5
1775059024,2040140,lws-main,47.70,477,46437
1775059024,2040150,db-read-1,0.00,0,60
1775059024,2040151,db-read-2,0.00,0,57
1775059024,2040152,db-read-3,0.00,0,71
1775059024,2040153,db-read-4,0.00,0,56
1775059024,2040154,db-write,0.00,0,2
1775059024,2040155,event-worker,0.00,0,5
1775059034,2040140,lws-main,99.50,995,47432
1775059034,2040150,db-read-1,0.10,1,61
1775059034,2040151,db-read-2,0.10,1,58
1775059034,2040152,db-read-3,0.10,1,72
1775059034,2040153,db-read-4,0.00,0,56
1775059034,2040154,db-write,0.10,1,3
1775059034,2040155,event-worker,0.00,0,5
1775059044,2040140,lws-main,88.50,885,48317
1775059044,2040150,db-read-1,0.20,2,63
1775059044,2040151,db-read-2,0.10,1,59
1775059044,2040152,db-read-3,0.10,1,73
1775059044,2040153,db-read-4,0.20,2,58
1775059044,2040154,db-write,0.00,0,3
1775059044,2040155,event-worker,0.00,0,5
1775059054,2040140,lws-main,67.70,677,48994
1775059054,2040150,db-read-1,0.00,0,63
1775059054,2040151,db-read-2,0.00,0,59
1775059054,2040152,db-read-3,0.00,0,73
1775059054,2040153,db-read-4,0.00,0,58
1775059054,2040154,db-write,0.00,0,3
1775059054,2040155,event-worker,0.00,0,5
1775059064,2040140,lws-main,53.10,531,49525
1775059064,2040150,db-read-1,0.40,4,67
1775059064,2040151,db-read-2,0.40,4,63
1775059064,2040152,db-read-3,0.20,2,75
1775059064,2040153,db-read-4,0.00,0,58
1775059064,2040154,db-write,0.00,0,3
1775059064,2040155,event-worker,0.00,0,5
1775059074,2040140,lws-main,61.60,616,50141
1775059074,2040150,db-read-1,0.30,3,70
1775059074,2040151,db-read-2,0.20,2,65
1775059074,2040152,db-read-3,0.10,1,76
1775059074,2040153,db-read-4,0.20,2,60
1775059074,2040154,db-write,0.00,0,3
1775059074,2040155,event-worker,0.00,0,5
1775059084,2040140,lws-main,96.20,962,51103
1775059084,2040150,db-read-1,0.00,0,70
1775059084,2040151,db-read-2,0.20,2,67
1775059084,2040152,db-read-3,0.40,4,80
1775059084,2040153,db-read-4,0.10,1,61
1775059084,2040154,db-write,0.10,1,4
1775059084,2040155,event-worker,0.00,0,5
1775059094,2040140,lws-main,53.60,536,51639
1775059094,2040150,db-read-1,0.10,1,71
1775059094,2040151,db-read-2,0.20,2,69
1775059094,2040152,db-read-3,0.00,0,80
1775059094,2040153,db-read-4,0.00,0,61
1775059094,2040154,db-write,0.00,0,4
1775059094,2040155,event-worker,0.00,0,5
1775059105,2040140,lws-main,51.40,514,52153
1775059105,2040150,db-read-1,0.10,1,72
1775059105,2040151,db-read-2,0.00,0,69
1775059105,2040152,db-read-3,0.00,0,80
1775059105,2040153,db-read-4,0.10,1,62
1775059105,2040154,db-write,0.00,0,4
1775059105,2040155,event-worker,0.00,0,5
1775059115,2040140,lws-main,78.50,785,52938
1775059115,2040150,db-read-1,0.00,0,72
1775059115,2040151,db-read-2,0.00,0,69
1775059115,2040152,db-read-3,0.00,0,80
1775059115,2040153,db-read-4,0.00,0,62
1775059115,2040154,db-write,0.00,0,4
1775059115,2040155,event-worker,0.00,0,5
1775059125,2040140,lws-main,40.70,407,53345
1775059125,2040150,db-read-1,0.00,0,72
1775059125,2040151,db-read-2,0.00,0,69
1775059125,2040152,db-read-3,0.10,1,81
1775059125,2040153,db-read-4,0.10,1,63
1775059125,2040154,db-write,0.00,0,4
1775059125,2040155,event-worker,0.00,0,5
1775059135,2040140,lws-main,41.00,410,53755
1775059135,2040150,db-read-1,0.00,0,72
1775059135,2040151,db-read-2,0.00,0,69
1775059135,2040152,db-read-3,0.00,0,81
1775059135,2040153,db-read-4,0.00,0,63
1775059135,2040154,db-write,0.00,0,4
1775059135,2040155,event-worker,0.00,0,5
1775059145,2040140,lws-main,58.70,587,54342
1775059145,2040150,db-read-1,0.00,0,72
1775059145,2040151,db-read-2,0.00,0,69
1775059145,2040152,db-read-3,0.00,0,81
1775059145,2040153,db-read-4,0.00,0,63
1775059145,2040154,db-write,0.00,0,4
1775059145,2040155,event-worker,0.00,0,5
1775059155,2040140,lws-main,28.40,284,54626
1775059155,2040150,db-read-1,0.20,2,74
1775059155,2040151,db-read-2,0.20,2,71
1775059155,2040152,db-read-3,0.10,1,82
1775059155,2040153,db-read-4,0.10,1,64
1775059155,2040154,db-write,0.00,0,4
1775059155,2040155,event-worker,0.00,0,5
1775059165,2040140,lws-main,89.80,898,55524
1775059165,2040150,db-read-1,0.30,3,77
1775059165,2040151,db-read-2,0.20,2,73
1775059165,2040152,db-read-3,0.30,3,85
1775059165,2040153,db-read-4,0.10,1,65
1775059165,2040154,db-write,0.00,0,4
1775059165,2040155,event-worker,0.00,0,5
1775059175,2040140,lws-main,100.20,1002,56526
1775059175,2040150,db-read-1,0.00,0,77
1775059175,2040151,db-read-2,0.00,0,73
1775059175,2040152,db-read-3,0.10,1,86
1775059175,2040153,db-read-4,0.20,2,67
1775059175,2040154,db-write,0.00,0,4
1775059175,2040155,event-worker,0.00,0,5
1775059185,2040140,lws-main,100.20,1002,57528
1775059185,2040150,db-read-1,0.20,2,79
1775059185,2040151,db-read-2,0.20,2,75
1775059185,2040152,db-read-3,0.10,1,87
1775059185,2040153,db-read-4,0.20,2,69
1775059185,2040154,db-write,0.00,0,4
1775059185,2040155,event-worker,0.00,0,5
1775059195,2040140,lws-main,75.70,757,58285
1775059195,2040150,db-read-1,0.20,2,81
1775059195,2040151,db-read-2,0.20,2,77
1775059195,2040152,db-read-3,0.20,2,89
1775059195,2040153,db-read-4,0.20,2,71
1775059195,2040154,db-write,0.00,0,4
1775059195,2040155,event-worker,0.00,0,5
1775059205,2040140,lws-main,77.70,777,59062
1775059205,2040150,db-read-1,0.20,2,83
1775059205,2040151,db-read-2,0.30,3,80
1775059205,2040152,db-read-3,0.50,5,94
1775059205,2040153,db-read-4,0.30,3,74
1775059205,2040154,db-write,0.00,0,4
1775059205,2040155,event-worker,0.10,1,6
1775059215,2040140,lws-main,99.60,996,60058
1775059215,2040150,db-read-1,0.40,4,87
1775059215,2040151,db-read-2,0.40,4,84
1775059215,2040152,db-read-3,0.10,1,95
1775059215,2040153,db-read-4,0.20,2,76
1775059215,2040154,db-write,0.00,0,4
1775059215,2040155,event-worker,0.00,0,6
1775059225,2040140,lws-main,100.30,1003,61061
1775059225,2040150,db-read-1,0.00,0,87
1775059225,2040151,db-read-2,0.10,1,85
1775059225,2040152,db-read-3,0.10,1,96
1775059225,2040153,db-read-4,0.20,2,78
1775059225,2040154,db-write,0.00,0,4
1775059225,2040155,event-worker,0.00,0,6
1775059235,2040140,lws-main,100.10,1001,62062
1775059235,2040150,db-read-1,0.10,1,88
1775059235,2040151,db-read-2,0.00,0,85
1775059235,2040152,db-read-3,0.00,0,96
1775059235,2040153,db-read-4,0.00,0,78
1775059235,2040154,db-write,0.00,0,4
1775059235,2040155,event-worker,0.00,0,6
1775059245,2040140,lws-main,98.10,981,63043
1775059245,2040150,db-read-1,0.10,1,89
1775059245,2040151,db-read-2,0.00,0,85
1775059245,2040152,db-read-3,0.20,2,98
1775059245,2040153,db-read-4,0.10,1,79
1775059245,2040154,db-write,0.00,0,4
1775059245,2040155,event-worker,0.00,0,6
1775059255,2040140,lws-main,54.10,541,63584
1775059255,2040150,db-read-1,0.20,2,91
1775059255,2040151,db-read-2,0.50,5,90
1775059255,2040152,db-read-3,0.50,5,103
1775059255,2040153,db-read-4,0.50,5,84
1775059255,2040154,db-write,0.00,0,4
1775059255,2040155,event-worker,0.00,0,6
1775059265,2040140,lws-main,82.80,828,64412
1775059265,2040150,db-read-1,0.00,0,91
1775059265,2040151,db-read-2,0.00,0,90
1775059265,2040152,db-read-3,0.00,0,103
1775059265,2040153,db-read-4,0.00,0,84
1775059265,2040154,db-write,0.00,0,4
1775059265,2040155,event-worker,0.00,0,6
1775059275,2040140,lws-main,99.90,999,65411
1775059275,2040150,db-read-1,0.10,1,92
1775059275,2040151,db-read-2,0.00,0,90
1775059275,2040152,db-read-3,0.00,0,103
1775059275,2040153,db-read-4,0.00,0,84
1775059275,2040154,db-write,0.00,0,4
1775059275,2040155,event-worker,0.00,0,6
1775059285,2040140,lws-main,58.30,583,65994
1775059285,2040150,db-read-1,0.00,0,92
1775059285,2040151,db-read-2,0.10,1,91
1775059285,2040152,db-read-3,0.10,1,104
1775059285,2040153,db-read-4,0.00,0,84
1775059285,2040154,db-write,0.00,0,4
1775059285,2040155,event-worker,0.00,0,6
1775059295,2040140,lws-main,42.60,426,66420
1775059295,2040150,db-read-1,0.30,3,95
1775059295,2040151,db-read-2,0.10,1,92
1775059295,2040152,db-read-3,0.10,1,105
1775059295,2040153,db-read-4,0.20,2,86
1775059295,2040154,db-write,0.00,0,4
1775059295,2040155,event-worker,0.00,0,6
1775059305,2040140,lws-main,16.20,162,66582
1775059305,2040150,db-read-1,0.20,2,97
1775059305,2040151,db-read-2,0.00,0,92
1775059305,2040152,db-read-3,0.10,1,106
1775059305,2040153,db-read-4,0.10,1,87
1775059305,2040154,db-write,0.00,0,4
1775059305,2040155,event-worker,0.00,0,6
1775059315,2040140,lws-main,100.30,1003,67585
1775059315,2040150,db-read-1,0.00,0,97
1775059315,2040151,db-read-2,0.00,0,92
1775059315,2040152,db-read-3,0.00,0,106
1775059315,2040153,db-read-4,0.00,0,87
1775059315,2040154,db-write,0.00,0,4
1775059315,2040155,event-worker,0.00,0,6
1775059325,2040140,lws-main,66.00,660,68245
1775059325,2040150,db-read-1,0.20,2,99
1775059325,2040151,db-read-2,0.50,5,97
1775059325,2040152,db-read-3,0.20,2,108
1775059325,2040153,db-read-4,0.20,2,89
1775059325,2040154,db-write,0.00,0,4
1775059325,2040155,event-worker,0.00,0,6
1775059335,2040140,lws-main,21.70,217,68462
1775059335,2040150,db-read-1,0.00,0,99
1775059335,2040151,db-read-2,0.00,0,97
1775059335,2040152,db-read-3,0.00,0,108
1775059335,2040153,db-read-4,0.00,0,89
1775059335,2040154,db-write,0.00,0,4
1775059335,2040155,event-worker,0.00,0,6
1775059345,2040140,lws-main,47.00,470,68932
1775059345,2040150,db-read-1,0.30,3,102
1775059345,2040151,db-read-2,0.30,3,100
1775059345,2040152,db-read-3,0.90,9,117
1775059345,2040153,db-read-4,0.20,2,91
1775059345,2040154,db-write,0.00,0,4
1775059345,2040155,event-worker,0.00,0,6
1775059355,2040140,lws-main,54.40,544,69476
1775059355,2040150,db-read-1,0.00,0,102
1775059355,2040151,db-read-2,0.00,0,100
1775059355,2040152,db-read-3,0.00,0,117
1775059355,2040153,db-read-4,0.00,0,91
1775059355,2040154,db-write,0.00,0,4
1775059355,2040155,event-worker,0.00,0,6
1775059365,2040140,lws-main,99.70,997,70473
1775059365,2040150,db-read-1,0.10,1,103
1775059365,2040151,db-read-2,0.10,1,101
1775059365,2040152,db-read-3,0.00,0,117
1775059365,2040153,db-read-4,0.00,0,91
1775059365,2040154,db-write,0.00,0,4
1775059365,2040155,event-worker,0.00,0,6
1775059375,2040140,lws-main,46.20,462,70935
1775059375,2040150,db-read-1,0.20,2,105
1775059375,2040151,db-read-2,0.10,1,102
1775059375,2040152,db-read-3,0.40,4,121
1775059375,2040153,db-read-4,0.20,2,93
1775059375,2040154,db-write,0.00,0,4
1775059375,2040155,event-worker,0.00,0,6
1775059385,2040140,lws-main,69.00,690,71625
1775059385,2040150,db-read-1,0.10,1,106
1775059385,2040151,db-read-2,0.00,0,102
1775059385,2040152,db-read-3,0.10,1,122
1775059385,2040153,db-read-4,0.10,1,94
1775059385,2040154,db-write,0.00,0,4
1775059385,2040155,event-worker,0.00,0,6
1775059395,2040140,lws-main,71.50,715,72340
1775059395,2040150,db-read-1,0.00,0,106
1775059395,2040151,db-read-2,0.20,2,104
1775059395,2040152,db-read-3,0.10,1,123
1775059395,2040153,db-read-4,0.30,3,97
1775059395,2040154,db-write,0.00,0,4
1775059395,2040155,event-worker,0.00,0,6
1775059405,2040140,lws-main,100.10,1001,73341
1775059405,2040150,db-read-1,0.20,2,108
1775059405,2040151,db-read-2,0.00,0,104
1775059405,2040152,db-read-3,0.00,0,123
1775059405,2040153,db-read-4,0.20,2,99
1775059405,2040154,db-write,0.00,0,4
1775059405,2040155,event-worker,0.00,0,6
1775059415,2040140,lws-main,100.20,1002,74343
1775059415,2040150,db-read-1,0.30,3,111
1775059415,2040151,db-read-2,0.20,2,106
1775059415,2040152,db-read-3,0.20,2,125
1775059415,2040153,db-read-4,0.30,3,102
1775059415,2040154,db-write,0.10,1,5
1775059415,2040155,event-worker,0.00,0,6
1775059425,2040140,lws-main,97.40,974,75317
1775059425,2040150,db-read-1,0.10,1,112
1775059425,2040151,db-read-2,0.10,1,107
1775059425,2040152,db-read-3,0.30,3,128
1775059425,2040153,db-read-4,0.10,1,103
1775059425,2040154,db-write,0.00,0,5
1775059425,2040155,event-worker,0.00,0,6
1775059435,2040140,lws-main,80.60,806,76123
1775059435,2040150,db-read-1,0.10,1,113
1775059435,2040151,db-read-2,0.20,2,109
1775059435,2040152,db-read-3,0.10,1,129
1775059435,2040153,db-read-4,0.10,1,104
1775059435,2040154,db-write,0.00,0,5
1775059435,2040155,event-worker,0.00,0,6
1775059445,2040140,lws-main,40.70,407,76530
1775059445,2040150,db-read-1,0.10,1,114
1775059445,2040151,db-read-2,0.00,0,109
1775059445,2040152,db-read-3,0.20,2,131
1775059445,2040153,db-read-4,0.10,1,105
1775059445,2040154,db-write,0.00,0,5
1775059445,2040155,event-worker,0.00,0,6
1775059456,2040140,lws-main,56.00,560,77090
1775059456,2040150,db-read-1,0.00,0,114
1775059456,2040151,db-read-2,0.20,2,111
1775059456,2040152,db-read-3,0.00,0,131
1775059456,2040153,db-read-4,0.10,1,106
1775059456,2040154,db-write,0.00,0,5
1775059456,2040155,event-worker,0.00,0,6
1775059466,2040140,lws-main,53.20,532,77622
1775059466,2040150,db-read-1,0.00,0,114
1775059466,2040151,db-read-2,0.00,0,111
1775059466,2040152,db-read-3,0.00,0,131
1775059466,2040153,db-read-4,0.00,0,106
1775059466,2040154,db-write,0.00,0,5
1775059466,2040155,event-worker,0.00,0,6
1775059476,2040140,lws-main,96.70,967,78589
1775059476,2040150,db-read-1,0.00,0,114
1775059476,2040151,db-read-2,0.00,0,111
1775059476,2040152,db-read-3,0.00,0,131
1775059476,2040153,db-read-4,0.00,0,106
1775059476,2040154,db-write,0.00,0,5
1775059476,2040155,event-worker,0.00,0,6
1775059486,2040140,lws-main,67.60,676,79265
1775059486,2040150,db-read-1,0.20,2,116
1775059486,2040151,db-read-2,0.00,0,111
1775059486,2040152,db-read-3,0.00,0,131
1775059486,2040153,db-read-4,0.00,0,106
1775059486,2040154,db-write,0.00,0,5
1775059486,2040155,event-worker,0.00,0,6
1 timestamp tid thread_name cpu_pct delta_ticks total_ticks
2 1775058964 2040140 lws-main 0.00 0 42849
3 1775058964 2040150 db-read-1 0.00 0 58
4 1775058964 2040151 db-read-2 0.00 0 54
5 1775058964 2040152 db-read-3 0.10 1 68
6 1775058964 2040153 db-read-4 0.00 0 53
7 1775058964 2040154 db-write 0.00 0 2
8 1775058964 2040155 event-worker 0.00 0 5
9 1775058974 2040140 lws-main 59.30 593 43442
10 1775058974 2040150 db-read-1 0.00 0 58
11 1775058974 2040151 db-read-2 0.00 0 54
12 1775058974 2040152 db-read-3 0.10 1 69
13 1775058974 2040153 db-read-4 0.00 0 53
14 1775058974 2040154 db-write 0.00 0 2
15 1775058974 2040155 event-worker 0.00 0 5
16 1775058984 2040140 lws-main 67.40 674 44116
17 1775058984 2040150 db-read-1 0.00 0 58
18 1775058984 2040151 db-read-2 0.00 0 54
19 1775058984 2040152 db-read-3 0.00 0 69
20 1775058984 2040153 db-read-4 0.10 1 54
21 1775058984 2040154 db-write 0.00 0 2
22 1775058984 2040155 event-worker 0.00 0 5
23 1775058994 2040140 lws-main 98.70 987 45103
24 1775058994 2040150 db-read-1 0.10 1 59
25 1775058994 2040151 db-read-2 0.00 0 54
26 1775058994 2040152 db-read-3 0.00 0 69
27 1775058994 2040153 db-read-4 0.00 0 54
28 1775058994 2040154 db-write 0.00 0 2
29 1775058994 2040155 event-worker 0.00 0 5
30 1775059004 2040140 lws-main 71.00 710 45813
31 1775059004 2040150 db-read-1 0.00 0 59
32 1775059004 2040151 db-read-2 0.00 0 54
33 1775059004 2040152 db-read-3 0.10 1 70
34 1775059004 2040153 db-read-4 0.10 1 55
35 1775059004 2040154 db-write 0.00 0 2
36 1775059004 2040155 event-worker 0.00 0 5
37 1775059014 2040140 lws-main 14.70 147 45960
38 1775059014 2040150 db-read-1 0.10 1 60
39 1775059014 2040151 db-read-2 0.30 3 57
40 1775059014 2040152 db-read-3 0.10 1 71
41 1775059014 2040153 db-read-4 0.10 1 56
42 1775059014 2040154 db-write 0.00 0 2
43 1775059014 2040155 event-worker 0.00 0 5
44 1775059024 2040140 lws-main 47.70 477 46437
45 1775059024 2040150 db-read-1 0.00 0 60
46 1775059024 2040151 db-read-2 0.00 0 57
47 1775059024 2040152 db-read-3 0.00 0 71
48 1775059024 2040153 db-read-4 0.00 0 56
49 1775059024 2040154 db-write 0.00 0 2
50 1775059024 2040155 event-worker 0.00 0 5
51 1775059034 2040140 lws-main 99.50 995 47432
52 1775059034 2040150 db-read-1 0.10 1 61
53 1775059034 2040151 db-read-2 0.10 1 58
54 1775059034 2040152 db-read-3 0.10 1 72
55 1775059034 2040153 db-read-4 0.00 0 56
56 1775059034 2040154 db-write 0.10 1 3
57 1775059034 2040155 event-worker 0.00 0 5
58 1775059044 2040140 lws-main 88.50 885 48317
59 1775059044 2040150 db-read-1 0.20 2 63
60 1775059044 2040151 db-read-2 0.10 1 59
61 1775059044 2040152 db-read-3 0.10 1 73
62 1775059044 2040153 db-read-4 0.20 2 58
63 1775059044 2040154 db-write 0.00 0 3
64 1775059044 2040155 event-worker 0.00 0 5
65 1775059054 2040140 lws-main 67.70 677 48994
66 1775059054 2040150 db-read-1 0.00 0 63
67 1775059054 2040151 db-read-2 0.00 0 59
68 1775059054 2040152 db-read-3 0.00 0 73
69 1775059054 2040153 db-read-4 0.00 0 58
70 1775059054 2040154 db-write 0.00 0 3
71 1775059054 2040155 event-worker 0.00 0 5
72 1775059064 2040140 lws-main 53.10 531 49525
73 1775059064 2040150 db-read-1 0.40 4 67
74 1775059064 2040151 db-read-2 0.40 4 63
75 1775059064 2040152 db-read-3 0.20 2 75
76 1775059064 2040153 db-read-4 0.00 0 58
77 1775059064 2040154 db-write 0.00 0 3
78 1775059064 2040155 event-worker 0.00 0 5
79 1775059074 2040140 lws-main 61.60 616 50141
80 1775059074 2040150 db-read-1 0.30 3 70
81 1775059074 2040151 db-read-2 0.20 2 65
82 1775059074 2040152 db-read-3 0.10 1 76
83 1775059074 2040153 db-read-4 0.20 2 60
84 1775059074 2040154 db-write 0.00 0 3
85 1775059074 2040155 event-worker 0.00 0 5
86 1775059084 2040140 lws-main 96.20 962 51103
87 1775059084 2040150 db-read-1 0.00 0 70
88 1775059084 2040151 db-read-2 0.20 2 67
89 1775059084 2040152 db-read-3 0.40 4 80
90 1775059084 2040153 db-read-4 0.10 1 61
91 1775059084 2040154 db-write 0.10 1 4
92 1775059084 2040155 event-worker 0.00 0 5
93 1775059094 2040140 lws-main 53.60 536 51639
94 1775059094 2040150 db-read-1 0.10 1 71
95 1775059094 2040151 db-read-2 0.20 2 69
96 1775059094 2040152 db-read-3 0.00 0 80
97 1775059094 2040153 db-read-4 0.00 0 61
98 1775059094 2040154 db-write 0.00 0 4
99 1775059094 2040155 event-worker 0.00 0 5
100 1775059105 2040140 lws-main 51.40 514 52153
101 1775059105 2040150 db-read-1 0.10 1 72
102 1775059105 2040151 db-read-2 0.00 0 69
103 1775059105 2040152 db-read-3 0.00 0 80
104 1775059105 2040153 db-read-4 0.10 1 62
105 1775059105 2040154 db-write 0.00 0 4
106 1775059105 2040155 event-worker 0.00 0 5
107 1775059115 2040140 lws-main 78.50 785 52938
108 1775059115 2040150 db-read-1 0.00 0 72
109 1775059115 2040151 db-read-2 0.00 0 69
110 1775059115 2040152 db-read-3 0.00 0 80
111 1775059115 2040153 db-read-4 0.00 0 62
112 1775059115 2040154 db-write 0.00 0 4
113 1775059115 2040155 event-worker 0.00 0 5
114 1775059125 2040140 lws-main 40.70 407 53345
115 1775059125 2040150 db-read-1 0.00 0 72
116 1775059125 2040151 db-read-2 0.00 0 69
117 1775059125 2040152 db-read-3 0.10 1 81
118 1775059125 2040153 db-read-4 0.10 1 63
119 1775059125 2040154 db-write 0.00 0 4
120 1775059125 2040155 event-worker 0.00 0 5
121 1775059135 2040140 lws-main 41.00 410 53755
122 1775059135 2040150 db-read-1 0.00 0 72
123 1775059135 2040151 db-read-2 0.00 0 69
124 1775059135 2040152 db-read-3 0.00 0 81
125 1775059135 2040153 db-read-4 0.00 0 63
126 1775059135 2040154 db-write 0.00 0 4
127 1775059135 2040155 event-worker 0.00 0 5
128 1775059145 2040140 lws-main 58.70 587 54342
129 1775059145 2040150 db-read-1 0.00 0 72
130 1775059145 2040151 db-read-2 0.00 0 69
131 1775059145 2040152 db-read-3 0.00 0 81
132 1775059145 2040153 db-read-4 0.00 0 63
133 1775059145 2040154 db-write 0.00 0 4
134 1775059145 2040155 event-worker 0.00 0 5
135 1775059155 2040140 lws-main 28.40 284 54626
136 1775059155 2040150 db-read-1 0.20 2 74
137 1775059155 2040151 db-read-2 0.20 2 71
138 1775059155 2040152 db-read-3 0.10 1 82
139 1775059155 2040153 db-read-4 0.10 1 64
140 1775059155 2040154 db-write 0.00 0 4
141 1775059155 2040155 event-worker 0.00 0 5
142 1775059165 2040140 lws-main 89.80 898 55524
143 1775059165 2040150 db-read-1 0.30 3 77
144 1775059165 2040151 db-read-2 0.20 2 73
145 1775059165 2040152 db-read-3 0.30 3 85
146 1775059165 2040153 db-read-4 0.10 1 65
147 1775059165 2040154 db-write 0.00 0 4
148 1775059165 2040155 event-worker 0.00 0 5
149 1775059175 2040140 lws-main 100.20 1002 56526
150 1775059175 2040150 db-read-1 0.00 0 77
151 1775059175 2040151 db-read-2 0.00 0 73
152 1775059175 2040152 db-read-3 0.10 1 86
153 1775059175 2040153 db-read-4 0.20 2 67
154 1775059175 2040154 db-write 0.00 0 4
155 1775059175 2040155 event-worker 0.00 0 5
156 1775059185 2040140 lws-main 100.20 1002 57528
157 1775059185 2040150 db-read-1 0.20 2 79
158 1775059185 2040151 db-read-2 0.20 2 75
159 1775059185 2040152 db-read-3 0.10 1 87
160 1775059185 2040153 db-read-4 0.20 2 69
161 1775059185 2040154 db-write 0.00 0 4
162 1775059185 2040155 event-worker 0.00 0 5
163 1775059195 2040140 lws-main 75.70 757 58285
164 1775059195 2040150 db-read-1 0.20 2 81
165 1775059195 2040151 db-read-2 0.20 2 77
166 1775059195 2040152 db-read-3 0.20 2 89
167 1775059195 2040153 db-read-4 0.20 2 71
168 1775059195 2040154 db-write 0.00 0 4
169 1775059195 2040155 event-worker 0.00 0 5
170 1775059205 2040140 lws-main 77.70 777 59062
171 1775059205 2040150 db-read-1 0.20 2 83
172 1775059205 2040151 db-read-2 0.30 3 80
173 1775059205 2040152 db-read-3 0.50 5 94
174 1775059205 2040153 db-read-4 0.30 3 74
175 1775059205 2040154 db-write 0.00 0 4
176 1775059205 2040155 event-worker 0.10 1 6
177 1775059215 2040140 lws-main 99.60 996 60058
178 1775059215 2040150 db-read-1 0.40 4 87
179 1775059215 2040151 db-read-2 0.40 4 84
180 1775059215 2040152 db-read-3 0.10 1 95
181 1775059215 2040153 db-read-4 0.20 2 76
182 1775059215 2040154 db-write 0.00 0 4
183 1775059215 2040155 event-worker 0.00 0 6
184 1775059225 2040140 lws-main 100.30 1003 61061
185 1775059225 2040150 db-read-1 0.00 0 87
186 1775059225 2040151 db-read-2 0.10 1 85
187 1775059225 2040152 db-read-3 0.10 1 96
188 1775059225 2040153 db-read-4 0.20 2 78
189 1775059225 2040154 db-write 0.00 0 4
190 1775059225 2040155 event-worker 0.00 0 6
191 1775059235 2040140 lws-main 100.10 1001 62062
192 1775059235 2040150 db-read-1 0.10 1 88
193 1775059235 2040151 db-read-2 0.00 0 85
194 1775059235 2040152 db-read-3 0.00 0 96
195 1775059235 2040153 db-read-4 0.00 0 78
196 1775059235 2040154 db-write 0.00 0 4
197 1775059235 2040155 event-worker 0.00 0 6
198 1775059245 2040140 lws-main 98.10 981 63043
199 1775059245 2040150 db-read-1 0.10 1 89
200 1775059245 2040151 db-read-2 0.00 0 85
201 1775059245 2040152 db-read-3 0.20 2 98
202 1775059245 2040153 db-read-4 0.10 1 79
203 1775059245 2040154 db-write 0.00 0 4
204 1775059245 2040155 event-worker 0.00 0 6
205 1775059255 2040140 lws-main 54.10 541 63584
206 1775059255 2040150 db-read-1 0.20 2 91
207 1775059255 2040151 db-read-2 0.50 5 90
208 1775059255 2040152 db-read-3 0.50 5 103
209 1775059255 2040153 db-read-4 0.50 5 84
210 1775059255 2040154 db-write 0.00 0 4
211 1775059255 2040155 event-worker 0.00 0 6
212 1775059265 2040140 lws-main 82.80 828 64412
213 1775059265 2040150 db-read-1 0.00 0 91
214 1775059265 2040151 db-read-2 0.00 0 90
215 1775059265 2040152 db-read-3 0.00 0 103
216 1775059265 2040153 db-read-4 0.00 0 84
217 1775059265 2040154 db-write 0.00 0 4
218 1775059265 2040155 event-worker 0.00 0 6
219 1775059275 2040140 lws-main 99.90 999 65411
220 1775059275 2040150 db-read-1 0.10 1 92
221 1775059275 2040151 db-read-2 0.00 0 90
222 1775059275 2040152 db-read-3 0.00 0 103
223 1775059275 2040153 db-read-4 0.00 0 84
224 1775059275 2040154 db-write 0.00 0 4
225 1775059275 2040155 event-worker 0.00 0 6
226 1775059285 2040140 lws-main 58.30 583 65994
227 1775059285 2040150 db-read-1 0.00 0 92
228 1775059285 2040151 db-read-2 0.10 1 91
229 1775059285 2040152 db-read-3 0.10 1 104
230 1775059285 2040153 db-read-4 0.00 0 84
231 1775059285 2040154 db-write 0.00 0 4
232 1775059285 2040155 event-worker 0.00 0 6
233 1775059295 2040140 lws-main 42.60 426 66420
234 1775059295 2040150 db-read-1 0.30 3 95
235 1775059295 2040151 db-read-2 0.10 1 92
236 1775059295 2040152 db-read-3 0.10 1 105
237 1775059295 2040153 db-read-4 0.20 2 86
238 1775059295 2040154 db-write 0.00 0 4
239 1775059295 2040155 event-worker 0.00 0 6
240 1775059305 2040140 lws-main 16.20 162 66582
241 1775059305 2040150 db-read-1 0.20 2 97
242 1775059305 2040151 db-read-2 0.00 0 92
243 1775059305 2040152 db-read-3 0.10 1 106
244 1775059305 2040153 db-read-4 0.10 1 87
245 1775059305 2040154 db-write 0.00 0 4
246 1775059305 2040155 event-worker 0.00 0 6
247 1775059315 2040140 lws-main 100.30 1003 67585
248 1775059315 2040150 db-read-1 0.00 0 97
249 1775059315 2040151 db-read-2 0.00 0 92
250 1775059315 2040152 db-read-3 0.00 0 106
251 1775059315 2040153 db-read-4 0.00 0 87
252 1775059315 2040154 db-write 0.00 0 4
253 1775059315 2040155 event-worker 0.00 0 6
254 1775059325 2040140 lws-main 66.00 660 68245
255 1775059325 2040150 db-read-1 0.20 2 99
256 1775059325 2040151 db-read-2 0.50 5 97
257 1775059325 2040152 db-read-3 0.20 2 108
258 1775059325 2040153 db-read-4 0.20 2 89
259 1775059325 2040154 db-write 0.00 0 4
260 1775059325 2040155 event-worker 0.00 0 6
261 1775059335 2040140 lws-main 21.70 217 68462
262 1775059335 2040150 db-read-1 0.00 0 99
263 1775059335 2040151 db-read-2 0.00 0 97
264 1775059335 2040152 db-read-3 0.00 0 108
265 1775059335 2040153 db-read-4 0.00 0 89
266 1775059335 2040154 db-write 0.00 0 4
267 1775059335 2040155 event-worker 0.00 0 6
268 1775059345 2040140 lws-main 47.00 470 68932
269 1775059345 2040150 db-read-1 0.30 3 102
270 1775059345 2040151 db-read-2 0.30 3 100
271 1775059345 2040152 db-read-3 0.90 9 117
272 1775059345 2040153 db-read-4 0.20 2 91
273 1775059345 2040154 db-write 0.00 0 4
274 1775059345 2040155 event-worker 0.00 0 6
275 1775059355 2040140 lws-main 54.40 544 69476
276 1775059355 2040150 db-read-1 0.00 0 102
277 1775059355 2040151 db-read-2 0.00 0 100
278 1775059355 2040152 db-read-3 0.00 0 117
279 1775059355 2040153 db-read-4 0.00 0 91
280 1775059355 2040154 db-write 0.00 0 4
281 1775059355 2040155 event-worker 0.00 0 6
282 1775059365 2040140 lws-main 99.70 997 70473
283 1775059365 2040150 db-read-1 0.10 1 103
284 1775059365 2040151 db-read-2 0.10 1 101
285 1775059365 2040152 db-read-3 0.00 0 117
286 1775059365 2040153 db-read-4 0.00 0 91
287 1775059365 2040154 db-write 0.00 0 4
288 1775059365 2040155 event-worker 0.00 0 6
289 1775059375 2040140 lws-main 46.20 462 70935
290 1775059375 2040150 db-read-1 0.20 2 105
291 1775059375 2040151 db-read-2 0.10 1 102
292 1775059375 2040152 db-read-3 0.40 4 121
293 1775059375 2040153 db-read-4 0.20 2 93
294 1775059375 2040154 db-write 0.00 0 4
295 1775059375 2040155 event-worker 0.00 0 6
296 1775059385 2040140 lws-main 69.00 690 71625
297 1775059385 2040150 db-read-1 0.10 1 106
298 1775059385 2040151 db-read-2 0.00 0 102
299 1775059385 2040152 db-read-3 0.10 1 122
300 1775059385 2040153 db-read-4 0.10 1 94
301 1775059385 2040154 db-write 0.00 0 4
302 1775059385 2040155 event-worker 0.00 0 6
303 1775059395 2040140 lws-main 71.50 715 72340
304 1775059395 2040150 db-read-1 0.00 0 106
305 1775059395 2040151 db-read-2 0.20 2 104
306 1775059395 2040152 db-read-3 0.10 1 123
307 1775059395 2040153 db-read-4 0.30 3 97
308 1775059395 2040154 db-write 0.00 0 4
309 1775059395 2040155 event-worker 0.00 0 6
310 1775059405 2040140 lws-main 100.10 1001 73341
311 1775059405 2040150 db-read-1 0.20 2 108
312 1775059405 2040151 db-read-2 0.00 0 104
313 1775059405 2040152 db-read-3 0.00 0 123
314 1775059405 2040153 db-read-4 0.20 2 99
315 1775059405 2040154 db-write 0.00 0 4
316 1775059405 2040155 event-worker 0.00 0 6
317 1775059415 2040140 lws-main 100.20 1002 74343
318 1775059415 2040150 db-read-1 0.30 3 111
319 1775059415 2040151 db-read-2 0.20 2 106
320 1775059415 2040152 db-read-3 0.20 2 125
321 1775059415 2040153 db-read-4 0.30 3 102
322 1775059415 2040154 db-write 0.10 1 5
323 1775059415 2040155 event-worker 0.00 0 6
324 1775059425 2040140 lws-main 97.40 974 75317
325 1775059425 2040150 db-read-1 0.10 1 112
326 1775059425 2040151 db-read-2 0.10 1 107
327 1775059425 2040152 db-read-3 0.30 3 128
328 1775059425 2040153 db-read-4 0.10 1 103
329 1775059425 2040154 db-write 0.00 0 5
330 1775059425 2040155 event-worker 0.00 0 6
331 1775059435 2040140 lws-main 80.60 806 76123
332 1775059435 2040150 db-read-1 0.10 1 113
333 1775059435 2040151 db-read-2 0.20 2 109
334 1775059435 2040152 db-read-3 0.10 1 129
335 1775059435 2040153 db-read-4 0.10 1 104
336 1775059435 2040154 db-write 0.00 0 5
337 1775059435 2040155 event-worker 0.00 0 6
338 1775059445 2040140 lws-main 40.70 407 76530
339 1775059445 2040150 db-read-1 0.10 1 114
340 1775059445 2040151 db-read-2 0.00 0 109
341 1775059445 2040152 db-read-3 0.20 2 131
342 1775059445 2040153 db-read-4 0.10 1 105
343 1775059445 2040154 db-write 0.00 0 5
344 1775059445 2040155 event-worker 0.00 0 6
345 1775059456 2040140 lws-main 56.00 560 77090
346 1775059456 2040150 db-read-1 0.00 0 114
347 1775059456 2040151 db-read-2 0.20 2 111
348 1775059456 2040152 db-read-3 0.00 0 131
349 1775059456 2040153 db-read-4 0.10 1 106
350 1775059456 2040154 db-write 0.00 0 5
351 1775059456 2040155 event-worker 0.00 0 6
352 1775059466 2040140 lws-main 53.20 532 77622
353 1775059466 2040150 db-read-1 0.00 0 114
354 1775059466 2040151 db-read-2 0.00 0 111
355 1775059466 2040152 db-read-3 0.00 0 131
356 1775059466 2040153 db-read-4 0.00 0 106
357 1775059466 2040154 db-write 0.00 0 5
358 1775059466 2040155 event-worker 0.00 0 6
359 1775059476 2040140 lws-main 96.70 967 78589
360 1775059476 2040150 db-read-1 0.00 0 114
361 1775059476 2040151 db-read-2 0.00 0 111
362 1775059476 2040152 db-read-3 0.00 0 131
363 1775059476 2040153 db-read-4 0.00 0 106
364 1775059476 2040154 db-write 0.00 0 5
365 1775059476 2040155 event-worker 0.00 0 6
366 1775059486 2040140 lws-main 67.60 676 79265
367 1775059486 2040150 db-read-1 0.20 2 116
368 1775059486 2040151 db-read-2 0.00 0 111
369 1775059486 2040152 db-read-3 0.00 0 131
370 1775059486 2040153 db-read-4 0.00 0 106
371 1775059486 2040154 db-write 0.00 0 5
372 1775059486 2040155 event-worker 0.00 0 6
@@ -0,0 +1,14 @@
==========================================
Thread CPU Summary (avg + max over run)
==========================================
Run timestamp (UTC): 20260401_155603
Duration: 600s, Interval: 10s
2040140 lws-main 68.71 100.30
2040152 db-read-3 0.12 0.90
2040150 db-read-1 0.11 0.40
2040151 db-read-2 0.11 0.50
2040153 db-read-4 0.10 0.50
2040154 db-write 0.01 0.10
2040155 event-worker 0.00 0.10
TID THREAD AVG_CPU% MAX_CPU%
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
Profiling PID: 2083904
CLK_TCK=100
Starting perf record in background...
perf_pid=2086924
Done. Artifacts in /tmp/c_relay_thread_profile_20260401_181751
@@ -0,0 +1,211 @@
timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks
1775067472,2083904,lws-main,0.00,0,17710
1775067472,2083906,db-read-1,0.00,0,33
1775067472,2083907,db-read-2,0.00,0,29
1775067472,2083908,db-read-3,0.00,0,31
1775067472,2083909,db-read-4,0.00,0,41
1775067472,2083910,db-write,0.00,0,0
1775067472,2083911,event-worker,0.00,0,0
1775067482,2083904,lws-main,53.40,534,18244
1775067482,2083906,db-read-1,0.20,2,35
1775067482,2083907,db-read-2,0.00,0,29
1775067482,2083908,db-read-3,0.10,1,32
1775067482,2083909,db-read-4,0.00,0,41
1775067482,2083910,db-write,0.00,0,0
1775067482,2083911,event-worker,0.00,0,0
1775067492,2083904,lws-main,100.30,1003,19247
1775067492,2083906,db-read-1,0.00,0,35
1775067492,2083907,db-read-2,0.00,0,29
1775067492,2083908,db-read-3,0.00,0,32
1775067492,2083909,db-read-4,0.00,0,41
1775067492,2083910,db-write,0.00,0,0
1775067492,2083911,event-worker,0.00,0,0
1775067502,2083904,lws-main,100.30,1003,20250
1775067502,2083906,db-read-1,0.00,0,35
1775067502,2083907,db-read-2,0.00,0,29
1775067502,2083908,db-read-3,0.00,0,32
1775067502,2083909,db-read-4,0.00,0,41
1775067502,2083910,db-write,0.00,0,0
1775067502,2083911,event-worker,0.00,0,0
1775067512,2083904,lws-main,95.70,957,21207
1775067512,2083906,db-read-1,0.00,0,35
1775067512,2083907,db-read-2,0.10,1,30
1775067512,2083908,db-read-3,0.00,0,32
1775067512,2083909,db-read-4,0.00,0,41
1775067512,2083910,db-write,0.00,0,0
1775067512,2083911,event-worker,0.00,0,0
1775067522,2083904,lws-main,73.70,737,21944
1775067522,2083906,db-read-1,0.00,0,35
1775067522,2083907,db-read-2,0.00,0,30
1775067522,2083908,db-read-3,0.00,0,32
1775067522,2083909,db-read-4,0.00,0,41
1775067522,2083910,db-write,0.00,0,0
1775067522,2083911,event-worker,0.00,0,0
1775067532,2083904,lws-main,9.80,98,22042
1775067532,2083906,db-read-1,0.00,0,35
1775067532,2083907,db-read-2,0.00,0,30
1775067532,2083908,db-read-3,0.10,1,33
1775067532,2083909,db-read-4,0.10,1,42
1775067532,2083910,db-write,0.00,0,0
1775067532,2083911,event-worker,0.00,0,0
1775067542,2083904,lws-main,28.80,288,22330
1775067542,2083906,db-read-1,0.20,2,37
1775067542,2083907,db-read-2,0.40,4,34
1775067542,2083908,db-read-3,0.10,1,34
1775067542,2083909,db-read-4,0.20,2,44
1775067542,2083910,db-write,0.00,0,0
1775067542,2083911,event-worker,0.00,0,0
1775067552,2083904,lws-main,37.80,378,22708
1775067552,2083906,db-read-1,0.20,2,39
1775067552,2083907,db-read-2,0.20,2,36
1775067552,2083908,db-read-3,0.40,4,38
1775067552,2083909,db-read-4,0.10,1,45
1775067552,2083910,db-write,0.00,0,0
1775067552,2083911,event-worker,0.00,0,0
1775067563,2083904,lws-main,89.40,894,23602
1775067563,2083906,db-read-1,0.00,0,39
1775067563,2083907,db-read-2,0.10,1,37
1775067563,2083908,db-read-3,0.00,0,38
1775067563,2083909,db-read-4,0.10,1,46
1775067563,2083910,db-write,0.00,0,0
1775067563,2083911,event-worker,0.00,0,0
1775067573,2083904,lws-main,99.50,995,24597
1775067573,2083906,db-read-1,0.00,0,39
1775067573,2083907,db-read-2,0.00,0,37
1775067573,2083908,db-read-3,0.00,0,38
1775067573,2083909,db-read-4,0.00,0,46
1775067573,2083910,db-write,0.00,0,0
1775067573,2083911,event-worker,0.00,0,0
1775067583,2083904,lws-main,100.20,1002,25599
1775067583,2083906,db-read-1,0.00,0,39
1775067583,2083907,db-read-2,0.00,0,37
1775067583,2083908,db-read-3,0.00,0,38
1775067583,2083909,db-read-4,0.00,0,46
1775067583,2083910,db-write,0.00,0,0
1775067583,2083911,event-worker,0.00,0,0
1775067593,2083904,lws-main,100.20,1002,26601
1775067593,2083906,db-read-1,0.00,0,39
1775067593,2083907,db-read-2,0.00,0,37
1775067593,2083908,db-read-3,0.00,0,38
1775067593,2083909,db-read-4,0.00,0,46
1775067593,2083910,db-write,0.00,0,0
1775067593,2083911,event-worker,0.00,0,0
1775067603,2083904,lws-main,89.20,892,27493
1775067603,2083906,db-read-1,0.60,6,45
1775067603,2083907,db-read-2,0.20,2,39
1775067603,2083908,db-read-3,0.40,4,42
1775067603,2083909,db-read-4,0.60,6,52
1775067603,2083910,db-write,0.00,0,0
1775067603,2083911,event-worker,0.00,0,0
1775067613,2083904,lws-main,76.10,761,28254
1775067613,2083906,db-read-1,0.00,0,45
1775067613,2083907,db-read-2,0.00,0,39
1775067613,2083908,db-read-3,0.00,0,42
1775067613,2083909,db-read-4,0.00,0,52
1775067613,2083910,db-write,0.00,0,0
1775067613,2083911,event-worker,0.00,0,0
1775067623,2083904,lws-main,97.90,979,29233
1775067623,2083906,db-read-1,0.10,1,46
1775067623,2083907,db-read-2,0.20,2,41
1775067623,2083908,db-read-3,0.00,0,42
1775067623,2083909,db-read-4,0.00,0,52
1775067623,2083910,db-write,0.00,0,0
1775067623,2083911,event-worker,0.00,0,0
1775067633,2083904,lws-main,45.40,454,29687
1775067633,2083906,db-read-1,0.00,0,46
1775067633,2083907,db-read-2,0.00,0,41
1775067633,2083908,db-read-3,0.10,1,43
1775067633,2083909,db-read-4,0.00,0,52
1775067633,2083910,db-write,0.00,0,0
1775067633,2083911,event-worker,0.00,0,0
1775067643,2083904,lws-main,100.40,1004,30691
1775067643,2083906,db-read-1,0.00,0,46
1775067643,2083907,db-read-2,0.00,0,41
1775067643,2083908,db-read-3,0.00,0,43
1775067643,2083909,db-read-4,0.00,0,52
1775067643,2083910,db-write,0.00,0,0
1775067643,2083911,event-worker,0.00,0,0
1775067653,2083904,lws-main,100.20,1002,31693
1775067653,2083906,db-read-1,0.10,1,47
1775067653,2083907,db-read-2,0.00,0,41
1775067653,2083908,db-read-3,0.10,1,44
1775067653,2083909,db-read-4,0.10,1,53
1775067653,2083910,db-write,0.00,0,0
1775067653,2083911,event-worker,0.00,0,0
1775067663,2083904,lws-main,68.40,684,32377
1775067663,2083906,db-read-1,0.10,1,48
1775067663,2083907,db-read-2,0.20,2,43
1775067663,2083908,db-read-3,0.10,1,45
1775067663,2083909,db-read-4,0.10,1,54
1775067663,2083910,db-write,0.00,0,0
1775067663,2083911,event-worker,0.00,0,0
1775067673,2083904,lws-main,29.60,296,32673
1775067673,2083906,db-read-1,0.10,1,49
1775067673,2083907,db-read-2,0.10,1,44
1775067673,2083908,db-read-3,0.20,2,47
1775067673,2083909,db-read-4,0.20,2,56
1775067673,2083910,db-write,0.00,0,0
1775067673,2083911,event-worker,0.00,0,0
1775067683,2083904,lws-main,58.30,583,33256
1775067683,2083906,db-read-1,0.40,4,53
1775067683,2083907,db-read-2,0.40,4,48
1775067683,2083908,db-read-3,0.40,4,51
1775067683,2083909,db-read-4,0.60,6,62
1775067683,2083910,db-write,0.00,0,0
1775067683,2083911,event-worker,0.10,1,1
1775067693,2083904,lws-main,52.90,529,33785
1775067693,2083906,db-read-1,0.10,1,54
1775067693,2083907,db-read-2,0.20,2,50
1775067693,2083908,db-read-3,0.10,1,52
1775067693,2083909,db-read-4,0.10,1,63
1775067693,2083910,db-write,0.00,0,0
1775067693,2083911,event-worker,0.00,0,1
1775067703,2083904,lws-main,43.10,431,34216
1775067703,2083906,db-read-1,0.10,1,55
1775067703,2083907,db-read-2,0.00,0,50
1775067703,2083908,db-read-3,0.00,0,52
1775067703,2083909,db-read-4,0.10,1,64
1775067703,2083910,db-write,0.00,0,0
1775067703,2083911,event-worker,0.00,0,1
1775067713,2083904,lws-main,60.20,602,34818
1775067713,2083906,db-read-1,0.00,0,55
1775067713,2083907,db-read-2,0.00,0,50
1775067713,2083908,db-read-3,0.00,0,52
1775067713,2083909,db-read-4,0.00,0,64
1775067713,2083910,db-write,0.00,0,0
1775067713,2083911,event-worker,0.00,0,1
1775067723,2083904,lws-main,100.40,1004,35822
1775067723,2083906,db-read-1,0.00,0,55
1775067723,2083907,db-read-2,0.00,0,50
1775067723,2083908,db-read-3,0.00,0,52
1775067723,2083909,db-read-4,0.00,0,64
1775067723,2083910,db-write,0.00,0,0
1775067723,2083911,event-worker,0.00,0,1
1775067733,2083904,lws-main,100.20,1002,36824
1775067733,2083906,db-read-1,0.00,0,55
1775067733,2083907,db-read-2,0.00,0,50
1775067733,2083908,db-read-3,0.00,0,52
1775067733,2083909,db-read-4,0.00,0,64
1775067733,2083910,db-write,0.00,0,0
1775067733,2083911,event-worker,0.00,0,1
1775067743,2083904,lws-main,100.20,1002,37826
1775067743,2083906,db-read-1,0.00,0,55
1775067743,2083907,db-read-2,0.00,0,50
1775067743,2083908,db-read-3,0.20,2,54
1775067743,2083909,db-read-4,0.00,0,64
1775067743,2083910,db-write,0.00,0,0
1775067743,2083911,event-worker,0.00,0,1
1775067753,2083904,lws-main,93.70,937,38763
1775067753,2083906,db-read-1,0.20,2,57
1775067753,2083907,db-read-2,0.20,2,52
1775067753,2083908,db-read-3,0.10,1,55
1775067753,2083909,db-read-4,0.20,2,66
1775067753,2083910,db-write,0.10,1,1
1775067753,2083911,event-worker,0.00,0,1
1775067763,2083904,lws-main,100.10,1001,39764
1775067763,2083906,db-read-1,0.00,0,57
1775067763,2083907,db-read-2,0.00,0,52
1775067763,2083908,db-read-3,0.00,0,55
1775067763,2083909,db-read-4,0.00,0,66
1775067763,2083910,db-write,0.00,0,1
1775067763,2083911,event-worker,0.00,0,1
1 timestamp tid thread_name cpu_pct delta_ticks total_ticks
2 1775067472 2083904 lws-main 0.00 0 17710
3 1775067472 2083906 db-read-1 0.00 0 33
4 1775067472 2083907 db-read-2 0.00 0 29
5 1775067472 2083908 db-read-3 0.00 0 31
6 1775067472 2083909 db-read-4 0.00 0 41
7 1775067472 2083910 db-write 0.00 0 0
8 1775067472 2083911 event-worker 0.00 0 0
9 1775067482 2083904 lws-main 53.40 534 18244
10 1775067482 2083906 db-read-1 0.20 2 35
11 1775067482 2083907 db-read-2 0.00 0 29
12 1775067482 2083908 db-read-3 0.10 1 32
13 1775067482 2083909 db-read-4 0.00 0 41
14 1775067482 2083910 db-write 0.00 0 0
15 1775067482 2083911 event-worker 0.00 0 0
16 1775067492 2083904 lws-main 100.30 1003 19247
17 1775067492 2083906 db-read-1 0.00 0 35
18 1775067492 2083907 db-read-2 0.00 0 29
19 1775067492 2083908 db-read-3 0.00 0 32
20 1775067492 2083909 db-read-4 0.00 0 41
21 1775067492 2083910 db-write 0.00 0 0
22 1775067492 2083911 event-worker 0.00 0 0
23 1775067502 2083904 lws-main 100.30 1003 20250
24 1775067502 2083906 db-read-1 0.00 0 35
25 1775067502 2083907 db-read-2 0.00 0 29
26 1775067502 2083908 db-read-3 0.00 0 32
27 1775067502 2083909 db-read-4 0.00 0 41
28 1775067502 2083910 db-write 0.00 0 0
29 1775067502 2083911 event-worker 0.00 0 0
30 1775067512 2083904 lws-main 95.70 957 21207
31 1775067512 2083906 db-read-1 0.00 0 35
32 1775067512 2083907 db-read-2 0.10 1 30
33 1775067512 2083908 db-read-3 0.00 0 32
34 1775067512 2083909 db-read-4 0.00 0 41
35 1775067512 2083910 db-write 0.00 0 0
36 1775067512 2083911 event-worker 0.00 0 0
37 1775067522 2083904 lws-main 73.70 737 21944
38 1775067522 2083906 db-read-1 0.00 0 35
39 1775067522 2083907 db-read-2 0.00 0 30
40 1775067522 2083908 db-read-3 0.00 0 32
41 1775067522 2083909 db-read-4 0.00 0 41
42 1775067522 2083910 db-write 0.00 0 0
43 1775067522 2083911 event-worker 0.00 0 0
44 1775067532 2083904 lws-main 9.80 98 22042
45 1775067532 2083906 db-read-1 0.00 0 35
46 1775067532 2083907 db-read-2 0.00 0 30
47 1775067532 2083908 db-read-3 0.10 1 33
48 1775067532 2083909 db-read-4 0.10 1 42
49 1775067532 2083910 db-write 0.00 0 0
50 1775067532 2083911 event-worker 0.00 0 0
51 1775067542 2083904 lws-main 28.80 288 22330
52 1775067542 2083906 db-read-1 0.20 2 37
53 1775067542 2083907 db-read-2 0.40 4 34
54 1775067542 2083908 db-read-3 0.10 1 34
55 1775067542 2083909 db-read-4 0.20 2 44
56 1775067542 2083910 db-write 0.00 0 0
57 1775067542 2083911 event-worker 0.00 0 0
58 1775067552 2083904 lws-main 37.80 378 22708
59 1775067552 2083906 db-read-1 0.20 2 39
60 1775067552 2083907 db-read-2 0.20 2 36
61 1775067552 2083908 db-read-3 0.40 4 38
62 1775067552 2083909 db-read-4 0.10 1 45
63 1775067552 2083910 db-write 0.00 0 0
64 1775067552 2083911 event-worker 0.00 0 0
65 1775067563 2083904 lws-main 89.40 894 23602
66 1775067563 2083906 db-read-1 0.00 0 39
67 1775067563 2083907 db-read-2 0.10 1 37
68 1775067563 2083908 db-read-3 0.00 0 38
69 1775067563 2083909 db-read-4 0.10 1 46
70 1775067563 2083910 db-write 0.00 0 0
71 1775067563 2083911 event-worker 0.00 0 0
72 1775067573 2083904 lws-main 99.50 995 24597
73 1775067573 2083906 db-read-1 0.00 0 39
74 1775067573 2083907 db-read-2 0.00 0 37
75 1775067573 2083908 db-read-3 0.00 0 38
76 1775067573 2083909 db-read-4 0.00 0 46
77 1775067573 2083910 db-write 0.00 0 0
78 1775067573 2083911 event-worker 0.00 0 0
79 1775067583 2083904 lws-main 100.20 1002 25599
80 1775067583 2083906 db-read-1 0.00 0 39
81 1775067583 2083907 db-read-2 0.00 0 37
82 1775067583 2083908 db-read-3 0.00 0 38
83 1775067583 2083909 db-read-4 0.00 0 46
84 1775067583 2083910 db-write 0.00 0 0
85 1775067583 2083911 event-worker 0.00 0 0
86 1775067593 2083904 lws-main 100.20 1002 26601
87 1775067593 2083906 db-read-1 0.00 0 39
88 1775067593 2083907 db-read-2 0.00 0 37
89 1775067593 2083908 db-read-3 0.00 0 38
90 1775067593 2083909 db-read-4 0.00 0 46
91 1775067593 2083910 db-write 0.00 0 0
92 1775067593 2083911 event-worker 0.00 0 0
93 1775067603 2083904 lws-main 89.20 892 27493
94 1775067603 2083906 db-read-1 0.60 6 45
95 1775067603 2083907 db-read-2 0.20 2 39
96 1775067603 2083908 db-read-3 0.40 4 42
97 1775067603 2083909 db-read-4 0.60 6 52
98 1775067603 2083910 db-write 0.00 0 0
99 1775067603 2083911 event-worker 0.00 0 0
100 1775067613 2083904 lws-main 76.10 761 28254
101 1775067613 2083906 db-read-1 0.00 0 45
102 1775067613 2083907 db-read-2 0.00 0 39
103 1775067613 2083908 db-read-3 0.00 0 42
104 1775067613 2083909 db-read-4 0.00 0 52
105 1775067613 2083910 db-write 0.00 0 0
106 1775067613 2083911 event-worker 0.00 0 0
107 1775067623 2083904 lws-main 97.90 979 29233
108 1775067623 2083906 db-read-1 0.10 1 46
109 1775067623 2083907 db-read-2 0.20 2 41
110 1775067623 2083908 db-read-3 0.00 0 42
111 1775067623 2083909 db-read-4 0.00 0 52
112 1775067623 2083910 db-write 0.00 0 0
113 1775067623 2083911 event-worker 0.00 0 0
114 1775067633 2083904 lws-main 45.40 454 29687
115 1775067633 2083906 db-read-1 0.00 0 46
116 1775067633 2083907 db-read-2 0.00 0 41
117 1775067633 2083908 db-read-3 0.10 1 43
118 1775067633 2083909 db-read-4 0.00 0 52
119 1775067633 2083910 db-write 0.00 0 0
120 1775067633 2083911 event-worker 0.00 0 0
121 1775067643 2083904 lws-main 100.40 1004 30691
122 1775067643 2083906 db-read-1 0.00 0 46
123 1775067643 2083907 db-read-2 0.00 0 41
124 1775067643 2083908 db-read-3 0.00 0 43
125 1775067643 2083909 db-read-4 0.00 0 52
126 1775067643 2083910 db-write 0.00 0 0
127 1775067643 2083911 event-worker 0.00 0 0
128 1775067653 2083904 lws-main 100.20 1002 31693
129 1775067653 2083906 db-read-1 0.10 1 47
130 1775067653 2083907 db-read-2 0.00 0 41
131 1775067653 2083908 db-read-3 0.10 1 44
132 1775067653 2083909 db-read-4 0.10 1 53
133 1775067653 2083910 db-write 0.00 0 0
134 1775067653 2083911 event-worker 0.00 0 0
135 1775067663 2083904 lws-main 68.40 684 32377
136 1775067663 2083906 db-read-1 0.10 1 48
137 1775067663 2083907 db-read-2 0.20 2 43
138 1775067663 2083908 db-read-3 0.10 1 45
139 1775067663 2083909 db-read-4 0.10 1 54
140 1775067663 2083910 db-write 0.00 0 0
141 1775067663 2083911 event-worker 0.00 0 0
142 1775067673 2083904 lws-main 29.60 296 32673
143 1775067673 2083906 db-read-1 0.10 1 49
144 1775067673 2083907 db-read-2 0.10 1 44
145 1775067673 2083908 db-read-3 0.20 2 47
146 1775067673 2083909 db-read-4 0.20 2 56
147 1775067673 2083910 db-write 0.00 0 0
148 1775067673 2083911 event-worker 0.00 0 0
149 1775067683 2083904 lws-main 58.30 583 33256
150 1775067683 2083906 db-read-1 0.40 4 53
151 1775067683 2083907 db-read-2 0.40 4 48
152 1775067683 2083908 db-read-3 0.40 4 51
153 1775067683 2083909 db-read-4 0.60 6 62
154 1775067683 2083910 db-write 0.00 0 0
155 1775067683 2083911 event-worker 0.10 1 1
156 1775067693 2083904 lws-main 52.90 529 33785
157 1775067693 2083906 db-read-1 0.10 1 54
158 1775067693 2083907 db-read-2 0.20 2 50
159 1775067693 2083908 db-read-3 0.10 1 52
160 1775067693 2083909 db-read-4 0.10 1 63
161 1775067693 2083910 db-write 0.00 0 0
162 1775067693 2083911 event-worker 0.00 0 1
163 1775067703 2083904 lws-main 43.10 431 34216
164 1775067703 2083906 db-read-1 0.10 1 55
165 1775067703 2083907 db-read-2 0.00 0 50
166 1775067703 2083908 db-read-3 0.00 0 52
167 1775067703 2083909 db-read-4 0.10 1 64
168 1775067703 2083910 db-write 0.00 0 0
169 1775067703 2083911 event-worker 0.00 0 1
170 1775067713 2083904 lws-main 60.20 602 34818
171 1775067713 2083906 db-read-1 0.00 0 55
172 1775067713 2083907 db-read-2 0.00 0 50
173 1775067713 2083908 db-read-3 0.00 0 52
174 1775067713 2083909 db-read-4 0.00 0 64
175 1775067713 2083910 db-write 0.00 0 0
176 1775067713 2083911 event-worker 0.00 0 1
177 1775067723 2083904 lws-main 100.40 1004 35822
178 1775067723 2083906 db-read-1 0.00 0 55
179 1775067723 2083907 db-read-2 0.00 0 50
180 1775067723 2083908 db-read-3 0.00 0 52
181 1775067723 2083909 db-read-4 0.00 0 64
182 1775067723 2083910 db-write 0.00 0 0
183 1775067723 2083911 event-worker 0.00 0 1
184 1775067733 2083904 lws-main 100.20 1002 36824
185 1775067733 2083906 db-read-1 0.00 0 55
186 1775067733 2083907 db-read-2 0.00 0 50
187 1775067733 2083908 db-read-3 0.00 0 52
188 1775067733 2083909 db-read-4 0.00 0 64
189 1775067733 2083910 db-write 0.00 0 0
190 1775067733 2083911 event-worker 0.00 0 1
191 1775067743 2083904 lws-main 100.20 1002 37826
192 1775067743 2083906 db-read-1 0.00 0 55
193 1775067743 2083907 db-read-2 0.00 0 50
194 1775067743 2083908 db-read-3 0.20 2 54
195 1775067743 2083909 db-read-4 0.00 0 64
196 1775067743 2083910 db-write 0.00 0 0
197 1775067743 2083911 event-worker 0.00 0 1
198 1775067753 2083904 lws-main 93.70 937 38763
199 1775067753 2083906 db-read-1 0.20 2 57
200 1775067753 2083907 db-read-2 0.20 2 52
201 1775067753 2083908 db-read-3 0.10 1 55
202 1775067753 2083909 db-read-4 0.20 2 66
203 1775067753 2083910 db-write 0.10 1 1
204 1775067753 2083911 event-worker 0.00 0 1
205 1775067763 2083904 lws-main 100.10 1001 39764
206 1775067763 2083906 db-read-1 0.00 0 57
207 1775067763 2083907 db-read-2 0.00 0 52
208 1775067763 2083908 db-read-3 0.00 0 55
209 1775067763 2083909 db-read-4 0.00 0 66
210 1775067763 2083910 db-write 0.00 0 1
211 1775067763 2083911 event-worker 0.00 0 1
@@ -0,0 +1,14 @@
==========================================
Thread CPU Summary (avg + max over run)
==========================================
Run timestamp (UTC): 20260401_181751
Duration: 300s, Interval: 10s
2083904 lws-main 73.51 100.40
2083906 db-read-1 0.08 0.60
2083907 db-read-2 0.08 0.40
2083908 db-read-3 0.08 0.40
2083909 db-read-4 0.08 0.60
2083910 db-write 0.00 0.10
2083911 event-worker 0.00 0.10
TID THREAD AVG_CPU% MAX_CPU%
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
Profiling PID: 2083904
CLK_TCK=100
Starting perf record in background...
perf_pid=2094527
Done. Artifacts in /tmp/c_relay_thread_profile_20260401_183846
@@ -0,0 +1,211 @@
timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks
1775068727,2083904,lws-main,0.20,2,102303
1775068727,2083906,db-read-1,0.00,0,153
1775068727,2083907,db-read-2,0.00,0,145
1775068727,2083908,db-read-3,0.00,0,145
1775068727,2083909,db-read-4,0.00,0,160
1775068727,2083910,db-write,0.00,0,4
1775068727,2083911,event-worker,0.00,0,11
1775068737,2083904,lws-main,100.20,1002,103305
1775068737,2083906,db-read-1,0.00,0,153
1775068737,2083907,db-read-2,0.10,1,146
1775068737,2083908,db-read-3,0.00,0,145
1775068737,2083909,db-read-4,0.00,0,160
1775068737,2083910,db-write,0.00,0,4
1775068737,2083911,event-worker,0.00,0,11
1775068747,2083904,lws-main,100.30,1003,104308
1775068747,2083906,db-read-1,0.00,0,153
1775068747,2083907,db-read-2,0.00,0,146
1775068747,2083908,db-read-3,0.10,1,146
1775068747,2083909,db-read-4,0.10,1,161
1775068747,2083910,db-write,0.00,0,4
1775068747,2083911,event-worker,0.00,0,11
1775068757,2083904,lws-main,100.20,1002,105310
1775068757,2083906,db-read-1,0.00,0,153
1775068757,2083907,db-read-2,0.00,0,146
1775068757,2083908,db-read-3,0.00,0,146
1775068757,2083909,db-read-4,0.00,0,161
1775068757,2083910,db-write,0.00,0,4
1775068757,2083911,event-worker,0.00,0,11
1775068767,2083904,lws-main,100.20,1002,106312
1775068767,2083906,db-read-1,0.00,0,153
1775068767,2083907,db-read-2,0.00,0,146
1775068767,2083908,db-read-3,0.00,0,146
1775068767,2083909,db-read-4,0.00,0,161
1775068767,2083910,db-write,0.00,0,4
1775068767,2083911,event-worker,0.00,0,11
1775068777,2083904,lws-main,81.10,811,107123
1775068777,2083906,db-read-1,0.00,0,153
1775068777,2083907,db-read-2,0.00,0,146
1775068777,2083908,db-read-3,0.10,1,147
1775068777,2083909,db-read-4,0.00,0,161
1775068777,2083910,db-write,0.00,0,4
1775068777,2083911,event-worker,0.00,0,11
1775068787,2083904,lws-main,7.40,74,107197
1775068787,2083906,db-read-1,0.00,0,153
1775068787,2083907,db-read-2,0.00,0,146
1775068787,2083908,db-read-3,0.00,0,147
1775068787,2083909,db-read-4,0.00,0,161
1775068787,2083910,db-write,0.00,0,4
1775068787,2083911,event-worker,0.00,0,11
1775068797,2083904,lws-main,50.30,503,107700
1775068797,2083906,db-read-1,0.30,3,156
1775068797,2083907,db-read-2,0.30,3,149
1775068797,2083908,db-read-3,0.30,3,150
1775068797,2083909,db-read-4,0.20,2,163
1775068797,2083910,db-write,0.00,0,4
1775068797,2083911,event-worker,0.00,0,11
1775068807,2083904,lws-main,58.00,580,108280
1775068807,2083906,db-read-1,0.00,0,156
1775068807,2083907,db-read-2,0.00,0,149
1775068807,2083908,db-read-3,0.00,0,150
1775068807,2083909,db-read-4,0.00,0,163
1775068807,2083910,db-write,0.00,0,4
1775068807,2083911,event-worker,0.00,0,11
1775068818,2083904,lws-main,58.40,584,108864
1775068818,2083906,db-read-1,0.10,1,157
1775068818,2083907,db-read-2,0.10,1,150
1775068818,2083908,db-read-3,0.10,1,151
1775068818,2083909,db-read-4,0.30,3,166
1775068818,2083910,db-write,0.00,0,4
1775068818,2083911,event-worker,0.00,0,11
1775068828,2083904,lws-main,43.30,433,109297
1775068828,2083906,db-read-1,0.00,0,157
1775068828,2083907,db-read-2,0.00,0,150
1775068828,2083908,db-read-3,0.00,0,151
1775068828,2083909,db-read-4,0.20,2,168
1775068828,2083910,db-write,0.00,0,4
1775068828,2083911,event-worker,0.20,2,13
1775068838,2083904,lws-main,29.00,290,109587
1775068838,2083906,db-read-1,0.20,2,159
1775068838,2083907,db-read-2,0.00,0,150
1775068838,2083908,db-read-3,0.10,1,152
1775068838,2083909,db-read-4,0.00,0,168
1775068838,2083910,db-write,0.00,0,4
1775068838,2083911,event-worker,0.00,0,13
1775068848,2083904,lws-main,37.00,370,109957
1775068848,2083906,db-read-1,0.00,0,159
1775068848,2083907,db-read-2,0.10,1,151
1775068848,2083908,db-read-3,0.00,0,152
1775068848,2083909,db-read-4,0.00,0,168
1775068848,2083910,db-write,0.00,0,4
1775068848,2083911,event-worker,0.00,0,13
1775068858,2083904,lws-main,48.40,484,110441
1775068858,2083906,db-read-1,0.10,1,160
1775068858,2083907,db-read-2,0.10,1,152
1775068858,2083908,db-read-3,0.10,1,153
1775068858,2083909,db-read-4,0.20,2,170
1775068858,2083910,db-write,0.00,0,4
1775068858,2083911,event-worker,0.00,0,13
1775068868,2083904,lws-main,45.00,450,110891
1775068868,2083906,db-read-1,0.00,0,160
1775068868,2083907,db-read-2,0.10,1,153
1775068868,2083908,db-read-3,0.00,0,153
1775068868,2083909,db-read-4,0.00,0,170
1775068868,2083910,db-write,0.00,0,4
1775068868,2083911,event-worker,0.00,0,13
1775068878,2083904,lws-main,21.00,210,111101
1775068878,2083906,db-read-1,0.00,0,160
1775068878,2083907,db-read-2,0.00,0,153
1775068878,2083908,db-read-3,0.00,0,153
1775068878,2083909,db-read-4,0.00,0,170
1775068878,2083910,db-write,0.00,0,4
1775068878,2083911,event-worker,0.00,0,13
1775068888,2083904,lws-main,36.40,364,111465
1775068888,2083906,db-read-1,0.00,0,160
1775068888,2083907,db-read-2,0.30,3,156
1775068888,2083908,db-read-3,0.10,1,154
1775068888,2083909,db-read-4,0.00,0,170
1775068888,2083910,db-write,0.00,0,4
1775068888,2083911,event-worker,0.00,0,13
1775068898,2083904,lws-main,44.10,441,111906
1775068898,2083906,db-read-1,0.00,0,160
1775068898,2083907,db-read-2,0.00,0,156
1775068898,2083908,db-read-3,0.00,0,154
1775068898,2083909,db-read-4,0.00,0,170
1775068898,2083910,db-write,0.00,0,4
1775068898,2083911,event-worker,0.00,0,13
1775068908,2083904,lws-main,19.70,197,112103
1775068908,2083906,db-read-1,0.20,2,162
1775068908,2083907,db-read-2,0.10,1,157
1775068908,2083908,db-read-3,0.00,0,154
1775068908,2083909,db-read-4,0.10,1,171
1775068908,2083910,db-write,0.00,0,4
1775068908,2083911,event-worker,0.00,0,13
1775068918,2083904,lws-main,83.20,832,112935
1775068918,2083906,db-read-1,0.30,3,165
1775068918,2083907,db-read-2,0.30,3,160
1775068918,2083908,db-read-3,0.20,2,156
1775068918,2083909,db-read-4,0.20,2,173
1775068918,2083910,db-write,0.00,0,4
1775068918,2083911,event-worker,0.00,0,13
1775068928,2083904,lws-main,37.40,374,113309
1775068928,2083906,db-read-1,0.00,0,165
1775068928,2083907,db-read-2,0.00,0,160
1775068928,2083908,db-read-3,0.10,1,157
1775068928,2083909,db-read-4,0.00,0,173
1775068928,2083910,db-write,0.00,0,4
1775068928,2083911,event-worker,0.00,0,13
1775068938,2083904,lws-main,59.80,598,113907
1775068938,2083906,db-read-1,0.00,0,165
1775068938,2083907,db-read-2,0.00,0,160
1775068938,2083908,db-read-3,0.10,1,158
1775068938,2083909,db-read-4,0.00,0,173
1775068938,2083910,db-write,0.00,0,4
1775068938,2083911,event-worker,0.00,0,13
1775068948,2083904,lws-main,61.20,612,114519
1775068948,2083906,db-read-1,0.30,3,168
1775068948,2083907,db-read-2,0.20,2,162
1775068948,2083908,db-read-3,0.10,1,159
1775068948,2083909,db-read-4,0.40,4,177
1775068948,2083910,db-write,0.00,0,4
1775068948,2083911,event-worker,0.10,1,14
1775068958,2083904,lws-main,65.30,653,115172
1775068958,2083906,db-read-1,0.00,0,168
1775068958,2083907,db-read-2,0.10,1,163
1775068958,2083908,db-read-3,0.10,1,160
1775068958,2083909,db-read-4,0.20,2,179
1775068958,2083910,db-write,0.00,0,4
1775068958,2083911,event-worker,0.00,0,14
1775068968,2083904,lws-main,23.00,230,115402
1775068968,2083906,db-read-1,0.00,0,168
1775068968,2083907,db-read-2,0.00,0,163
1775068968,2083908,db-read-3,0.00,0,160
1775068968,2083909,db-read-4,0.00,0,179
1775068968,2083910,db-write,0.00,0,4
1775068968,2083911,event-worker,0.00,0,14
1775068978,2083904,lws-main,79.80,798,116200
1775068978,2083906,db-read-1,0.40,4,172
1775068978,2083907,db-read-2,0.10,1,164
1775068978,2083908,db-read-3,0.10,1,161
1775068978,2083909,db-read-4,0.20,2,181
1775068978,2083910,db-write,0.00,0,4
1775068978,2083911,event-worker,0.00,0,14
1775068988,2083904,lws-main,70.70,707,116907
1775068988,2083906,db-read-1,0.00,0,172
1775068988,2083907,db-read-2,0.00,0,164
1775068988,2083908,db-read-3,0.10,1,162
1775068988,2083909,db-read-4,0.00,0,181
1775068988,2083910,db-write,0.00,0,4
1775068988,2083911,event-worker,0.00,0,14
1775068998,2083904,lws-main,23.00,230,117137
1775068998,2083906,db-read-1,0.00,0,172
1775068998,2083907,db-read-2,0.00,0,164
1775068998,2083908,db-read-3,0.00,0,162
1775068998,2083909,db-read-4,0.00,0,181
1775068998,2083910,db-write,0.00,0,4
1775068998,2083911,event-worker,0.00,0,14
1775069008,2083904,lws-main,66.00,660,117797
1775069008,2083906,db-read-1,0.30,3,175
1775069008,2083907,db-read-2,0.40,4,168
1775069008,2083908,db-read-3,0.40,4,166
1775069008,2083909,db-read-4,0.30,3,184
1775069008,2083910,db-write,0.00,0,4
1775069008,2083911,event-worker,0.00,0,14
1775069018,2083904,lws-main,80.00,800,118597
1775069018,2083906,db-read-1,0.00,0,175
1775069018,2083907,db-read-2,0.00,0,168
1775069018,2083908,db-read-3,0.00,0,166
1775069018,2083909,db-read-4,0.00,0,184
1775069018,2083910,db-write,0.00,0,4
1775069018,2083911,event-worker,0.00,0,14
1 timestamp tid thread_name cpu_pct delta_ticks total_ticks
2 1775068727 2083904 lws-main 0.20 2 102303
3 1775068727 2083906 db-read-1 0.00 0 153
4 1775068727 2083907 db-read-2 0.00 0 145
5 1775068727 2083908 db-read-3 0.00 0 145
6 1775068727 2083909 db-read-4 0.00 0 160
7 1775068727 2083910 db-write 0.00 0 4
8 1775068727 2083911 event-worker 0.00 0 11
9 1775068737 2083904 lws-main 100.20 1002 103305
10 1775068737 2083906 db-read-1 0.00 0 153
11 1775068737 2083907 db-read-2 0.10 1 146
12 1775068737 2083908 db-read-3 0.00 0 145
13 1775068737 2083909 db-read-4 0.00 0 160
14 1775068737 2083910 db-write 0.00 0 4
15 1775068737 2083911 event-worker 0.00 0 11
16 1775068747 2083904 lws-main 100.30 1003 104308
17 1775068747 2083906 db-read-1 0.00 0 153
18 1775068747 2083907 db-read-2 0.00 0 146
19 1775068747 2083908 db-read-3 0.10 1 146
20 1775068747 2083909 db-read-4 0.10 1 161
21 1775068747 2083910 db-write 0.00 0 4
22 1775068747 2083911 event-worker 0.00 0 11
23 1775068757 2083904 lws-main 100.20 1002 105310
24 1775068757 2083906 db-read-1 0.00 0 153
25 1775068757 2083907 db-read-2 0.00 0 146
26 1775068757 2083908 db-read-3 0.00 0 146
27 1775068757 2083909 db-read-4 0.00 0 161
28 1775068757 2083910 db-write 0.00 0 4
29 1775068757 2083911 event-worker 0.00 0 11
30 1775068767 2083904 lws-main 100.20 1002 106312
31 1775068767 2083906 db-read-1 0.00 0 153
32 1775068767 2083907 db-read-2 0.00 0 146
33 1775068767 2083908 db-read-3 0.00 0 146
34 1775068767 2083909 db-read-4 0.00 0 161
35 1775068767 2083910 db-write 0.00 0 4
36 1775068767 2083911 event-worker 0.00 0 11
37 1775068777 2083904 lws-main 81.10 811 107123
38 1775068777 2083906 db-read-1 0.00 0 153
39 1775068777 2083907 db-read-2 0.00 0 146
40 1775068777 2083908 db-read-3 0.10 1 147
41 1775068777 2083909 db-read-4 0.00 0 161
42 1775068777 2083910 db-write 0.00 0 4
43 1775068777 2083911 event-worker 0.00 0 11
44 1775068787 2083904 lws-main 7.40 74 107197
45 1775068787 2083906 db-read-1 0.00 0 153
46 1775068787 2083907 db-read-2 0.00 0 146
47 1775068787 2083908 db-read-3 0.00 0 147
48 1775068787 2083909 db-read-4 0.00 0 161
49 1775068787 2083910 db-write 0.00 0 4
50 1775068787 2083911 event-worker 0.00 0 11
51 1775068797 2083904 lws-main 50.30 503 107700
52 1775068797 2083906 db-read-1 0.30 3 156
53 1775068797 2083907 db-read-2 0.30 3 149
54 1775068797 2083908 db-read-3 0.30 3 150
55 1775068797 2083909 db-read-4 0.20 2 163
56 1775068797 2083910 db-write 0.00 0 4
57 1775068797 2083911 event-worker 0.00 0 11
58 1775068807 2083904 lws-main 58.00 580 108280
59 1775068807 2083906 db-read-1 0.00 0 156
60 1775068807 2083907 db-read-2 0.00 0 149
61 1775068807 2083908 db-read-3 0.00 0 150
62 1775068807 2083909 db-read-4 0.00 0 163
63 1775068807 2083910 db-write 0.00 0 4
64 1775068807 2083911 event-worker 0.00 0 11
65 1775068818 2083904 lws-main 58.40 584 108864
66 1775068818 2083906 db-read-1 0.10 1 157
67 1775068818 2083907 db-read-2 0.10 1 150
68 1775068818 2083908 db-read-3 0.10 1 151
69 1775068818 2083909 db-read-4 0.30 3 166
70 1775068818 2083910 db-write 0.00 0 4
71 1775068818 2083911 event-worker 0.00 0 11
72 1775068828 2083904 lws-main 43.30 433 109297
73 1775068828 2083906 db-read-1 0.00 0 157
74 1775068828 2083907 db-read-2 0.00 0 150
75 1775068828 2083908 db-read-3 0.00 0 151
76 1775068828 2083909 db-read-4 0.20 2 168
77 1775068828 2083910 db-write 0.00 0 4
78 1775068828 2083911 event-worker 0.20 2 13
79 1775068838 2083904 lws-main 29.00 290 109587
80 1775068838 2083906 db-read-1 0.20 2 159
81 1775068838 2083907 db-read-2 0.00 0 150
82 1775068838 2083908 db-read-3 0.10 1 152
83 1775068838 2083909 db-read-4 0.00 0 168
84 1775068838 2083910 db-write 0.00 0 4
85 1775068838 2083911 event-worker 0.00 0 13
86 1775068848 2083904 lws-main 37.00 370 109957
87 1775068848 2083906 db-read-1 0.00 0 159
88 1775068848 2083907 db-read-2 0.10 1 151
89 1775068848 2083908 db-read-3 0.00 0 152
90 1775068848 2083909 db-read-4 0.00 0 168
91 1775068848 2083910 db-write 0.00 0 4
92 1775068848 2083911 event-worker 0.00 0 13
93 1775068858 2083904 lws-main 48.40 484 110441
94 1775068858 2083906 db-read-1 0.10 1 160
95 1775068858 2083907 db-read-2 0.10 1 152
96 1775068858 2083908 db-read-3 0.10 1 153
97 1775068858 2083909 db-read-4 0.20 2 170
98 1775068858 2083910 db-write 0.00 0 4
99 1775068858 2083911 event-worker 0.00 0 13
100 1775068868 2083904 lws-main 45.00 450 110891
101 1775068868 2083906 db-read-1 0.00 0 160
102 1775068868 2083907 db-read-2 0.10 1 153
103 1775068868 2083908 db-read-3 0.00 0 153
104 1775068868 2083909 db-read-4 0.00 0 170
105 1775068868 2083910 db-write 0.00 0 4
106 1775068868 2083911 event-worker 0.00 0 13
107 1775068878 2083904 lws-main 21.00 210 111101
108 1775068878 2083906 db-read-1 0.00 0 160
109 1775068878 2083907 db-read-2 0.00 0 153
110 1775068878 2083908 db-read-3 0.00 0 153
111 1775068878 2083909 db-read-4 0.00 0 170
112 1775068878 2083910 db-write 0.00 0 4
113 1775068878 2083911 event-worker 0.00 0 13
114 1775068888 2083904 lws-main 36.40 364 111465
115 1775068888 2083906 db-read-1 0.00 0 160
116 1775068888 2083907 db-read-2 0.30 3 156
117 1775068888 2083908 db-read-3 0.10 1 154
118 1775068888 2083909 db-read-4 0.00 0 170
119 1775068888 2083910 db-write 0.00 0 4
120 1775068888 2083911 event-worker 0.00 0 13
121 1775068898 2083904 lws-main 44.10 441 111906
122 1775068898 2083906 db-read-1 0.00 0 160
123 1775068898 2083907 db-read-2 0.00 0 156
124 1775068898 2083908 db-read-3 0.00 0 154
125 1775068898 2083909 db-read-4 0.00 0 170
126 1775068898 2083910 db-write 0.00 0 4
127 1775068898 2083911 event-worker 0.00 0 13
128 1775068908 2083904 lws-main 19.70 197 112103
129 1775068908 2083906 db-read-1 0.20 2 162
130 1775068908 2083907 db-read-2 0.10 1 157
131 1775068908 2083908 db-read-3 0.00 0 154
132 1775068908 2083909 db-read-4 0.10 1 171
133 1775068908 2083910 db-write 0.00 0 4
134 1775068908 2083911 event-worker 0.00 0 13
135 1775068918 2083904 lws-main 83.20 832 112935
136 1775068918 2083906 db-read-1 0.30 3 165
137 1775068918 2083907 db-read-2 0.30 3 160
138 1775068918 2083908 db-read-3 0.20 2 156
139 1775068918 2083909 db-read-4 0.20 2 173
140 1775068918 2083910 db-write 0.00 0 4
141 1775068918 2083911 event-worker 0.00 0 13
142 1775068928 2083904 lws-main 37.40 374 113309
143 1775068928 2083906 db-read-1 0.00 0 165
144 1775068928 2083907 db-read-2 0.00 0 160
145 1775068928 2083908 db-read-3 0.10 1 157
146 1775068928 2083909 db-read-4 0.00 0 173
147 1775068928 2083910 db-write 0.00 0 4
148 1775068928 2083911 event-worker 0.00 0 13
149 1775068938 2083904 lws-main 59.80 598 113907
150 1775068938 2083906 db-read-1 0.00 0 165
151 1775068938 2083907 db-read-2 0.00 0 160
152 1775068938 2083908 db-read-3 0.10 1 158
153 1775068938 2083909 db-read-4 0.00 0 173
154 1775068938 2083910 db-write 0.00 0 4
155 1775068938 2083911 event-worker 0.00 0 13
156 1775068948 2083904 lws-main 61.20 612 114519
157 1775068948 2083906 db-read-1 0.30 3 168
158 1775068948 2083907 db-read-2 0.20 2 162
159 1775068948 2083908 db-read-3 0.10 1 159
160 1775068948 2083909 db-read-4 0.40 4 177
161 1775068948 2083910 db-write 0.00 0 4
162 1775068948 2083911 event-worker 0.10 1 14
163 1775068958 2083904 lws-main 65.30 653 115172
164 1775068958 2083906 db-read-1 0.00 0 168
165 1775068958 2083907 db-read-2 0.10 1 163
166 1775068958 2083908 db-read-3 0.10 1 160
167 1775068958 2083909 db-read-4 0.20 2 179
168 1775068958 2083910 db-write 0.00 0 4
169 1775068958 2083911 event-worker 0.00 0 14
170 1775068968 2083904 lws-main 23.00 230 115402
171 1775068968 2083906 db-read-1 0.00 0 168
172 1775068968 2083907 db-read-2 0.00 0 163
173 1775068968 2083908 db-read-3 0.00 0 160
174 1775068968 2083909 db-read-4 0.00 0 179
175 1775068968 2083910 db-write 0.00 0 4
176 1775068968 2083911 event-worker 0.00 0 14
177 1775068978 2083904 lws-main 79.80 798 116200
178 1775068978 2083906 db-read-1 0.40 4 172
179 1775068978 2083907 db-read-2 0.10 1 164
180 1775068978 2083908 db-read-3 0.10 1 161
181 1775068978 2083909 db-read-4 0.20 2 181
182 1775068978 2083910 db-write 0.00 0 4
183 1775068978 2083911 event-worker 0.00 0 14
184 1775068988 2083904 lws-main 70.70 707 116907
185 1775068988 2083906 db-read-1 0.00 0 172
186 1775068988 2083907 db-read-2 0.00 0 164
187 1775068988 2083908 db-read-3 0.10 1 162
188 1775068988 2083909 db-read-4 0.00 0 181
189 1775068988 2083910 db-write 0.00 0 4
190 1775068988 2083911 event-worker 0.00 0 14
191 1775068998 2083904 lws-main 23.00 230 117137
192 1775068998 2083906 db-read-1 0.00 0 172
193 1775068998 2083907 db-read-2 0.00 0 164
194 1775068998 2083908 db-read-3 0.00 0 162
195 1775068998 2083909 db-read-4 0.00 0 181
196 1775068998 2083910 db-write 0.00 0 4
197 1775068998 2083911 event-worker 0.00 0 14
198 1775069008 2083904 lws-main 66.00 660 117797
199 1775069008 2083906 db-read-1 0.30 3 175
200 1775069008 2083907 db-read-2 0.40 4 168
201 1775069008 2083908 db-read-3 0.40 4 166
202 1775069008 2083909 db-read-4 0.30 3 184
203 1775069008 2083910 db-write 0.00 0 4
204 1775069008 2083911 event-worker 0.00 0 14
205 1775069018 2083904 lws-main 80.00 800 118597
206 1775069018 2083906 db-read-1 0.00 0 175
207 1775069018 2083907 db-read-2 0.00 0 168
208 1775069018 2083908 db-read-3 0.00 0 166
209 1775069018 2083909 db-read-4 0.00 0 184
210 1775069018 2083910 db-write 0.00 0 4
211 1775069018 2083911 event-worker 0.00 0 14
@@ -0,0 +1,14 @@
==========================================
Thread CPU Summary (avg + max over run)
==========================================
Run timestamp (UTC): 20260401_183846
Duration: 300s, Interval: 10s
2083904 lws-main 54.32 100.30
2083907 db-read-2 0.08 0.40
2083909 db-read-4 0.08 0.40
2083906 db-read-1 0.07 0.40
2083908 db-read-3 0.07 0.40
2083911 event-worker 0.01 0.20
2083910 db-write 0.00 0.00
TID THREAD AVG_CPU% MAX_CPU%
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
Profiling PID: 2115498
CLK_TCK=100
Starting perf record in background...
perf_pid=2116380
Done. Artifacts in /tmp/c_relay_thread_profile_20260401_193216
@@ -0,0 +1,211 @@
timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks
1775071937,2115498,lws-main,0.00,0,2521
1775071937,2115501,db-read-1,0.00,0,9
1775071937,2115502,db-read-2,0.00,0,10
1775071937,2115503,db-read-3,0.00,0,9
1775071937,2115504,db-read-4,0.00,0,9
1775071937,2115505,db-write,0.00,0,0
1775071937,2115506,event-worker,0.00,0,0
1775071947,2115498,lws-main,0.10,1,2522
1775071947,2115501,db-read-1,0.10,1,10
1775071947,2115502,db-read-2,0.00,0,10
1775071947,2115503,db-read-3,0.00,0,9
1775071947,2115504,db-read-4,0.30,3,12
1775071947,2115505,db-write,0.00,0,0
1775071947,2115506,event-worker,0.00,0,0
1775071957,2115498,lws-main,26.80,268,2790
1775071957,2115501,db-read-1,0.00,0,10
1775071957,2115502,db-read-2,0.10,1,11
1775071957,2115503,db-read-3,0.00,0,9
1775071957,2115504,db-read-4,0.00,0,12
1775071957,2115505,db-write,0.00,0,0
1775071957,2115506,event-worker,0.00,0,0
1775071967,2115498,lws-main,72.10,721,3511
1775071967,2115501,db-read-1,0.00,0,10
1775071967,2115502,db-read-2,0.00,0,11
1775071967,2115503,db-read-3,0.00,0,9
1775071967,2115504,db-read-4,0.00,0,12
1775071967,2115505,db-write,0.00,0,0
1775071967,2115506,event-worker,0.00,0,0
1775071977,2115498,lws-main,64.40,644,4155
1775071977,2115501,db-read-1,0.20,2,12
1775071977,2115502,db-read-2,0.10,1,12
1775071977,2115503,db-read-3,0.10,1,10
1775071977,2115504,db-read-4,0.20,2,14
1775071977,2115505,db-write,0.00,0,0
1775071977,2115506,event-worker,0.00,0,0
1775071987,2115498,lws-main,42.60,426,4581
1775071987,2115501,db-read-1,0.10,1,13
1775071987,2115502,db-read-2,0.10,1,13
1775071987,2115503,db-read-3,0.10,1,11
1775071987,2115504,db-read-4,0.00,0,14
1775071987,2115505,db-write,0.00,0,0
1775071987,2115506,event-worker,0.00,0,0
1775071997,2115498,lws-main,7.20,72,4653
1775071997,2115501,db-read-1,0.00,0,13
1775071997,2115502,db-read-2,0.00,0,13
1775071997,2115503,db-read-3,0.00,0,11
1775071997,2115504,db-read-4,0.00,0,14
1775071997,2115505,db-write,0.00,0,0
1775071997,2115506,event-worker,0.00,0,0
1775072007,2115498,lws-main,42.90,429,5082
1775072007,2115501,db-read-1,0.00,0,13
1775072007,2115502,db-read-2,0.00,0,13
1775072007,2115503,db-read-3,0.00,0,11
1775072007,2115504,db-read-4,0.00,0,14
1775072007,2115505,db-write,0.00,0,0
1775072007,2115506,event-worker,0.00,0,0
1775072017,2115498,lws-main,38.10,381,5463
1775072017,2115501,db-read-1,0.10,1,14
1775072017,2115502,db-read-2,0.20,2,15
1775072017,2115503,db-read-3,0.40,4,15
1775072017,2115504,db-read-4,0.10,1,15
1775072017,2115505,db-write,0.00,0,0
1775072017,2115506,event-worker,0.00,0,0
1775072027,2115498,lws-main,93.20,932,6395
1775072027,2115501,db-read-1,0.40,4,18
1775072027,2115502,db-read-2,0.40,4,19
1775072027,2115503,db-read-3,0.00,0,15
1775072027,2115504,db-read-4,0.10,1,16
1775072027,2115505,db-write,0.00,0,0
1775072027,2115506,event-worker,0.00,0,0
1775072037,2115498,lws-main,95.10,951,7346
1775072037,2115501,db-read-1,0.10,1,19
1775072037,2115502,db-read-2,0.20,2,21
1775072037,2115503,db-read-3,0.30,3,18
1775072037,2115504,db-read-4,0.00,0,16
1775072037,2115505,db-write,0.10,1,1
1775072037,2115506,event-worker,0.00,0,0
1775072047,2115498,lws-main,65.00,650,7996
1775072047,2115501,db-read-1,0.00,0,19
1775072047,2115502,db-read-2,0.00,0,21
1775072047,2115503,db-read-3,0.10,1,19
1775072047,2115504,db-read-4,0.10,1,17
1775072047,2115505,db-write,0.00,0,1
1775072047,2115506,event-worker,0.00,0,0
1775072057,2115498,lws-main,62.80,628,8624
1775072057,2115501,db-read-1,0.00,0,19
1775072057,2115502,db-read-2,0.00,0,21
1775072057,2115503,db-read-3,0.00,0,19
1775072057,2115504,db-read-4,0.10,1,18
1775072057,2115505,db-write,0.00,0,1
1775072057,2115506,event-worker,0.00,0,0
1775072068,2115498,lws-main,20.80,208,8832
1775072068,2115501,db-read-1,0.10,1,20
1775072068,2115502,db-read-2,0.00,0,21
1775072068,2115503,db-read-3,0.00,0,19
1775072068,2115504,db-read-4,0.00,0,18
1775072068,2115505,db-write,0.00,0,1
1775072068,2115506,event-worker,0.00,0,0
1775072078,2115498,lws-main,52.30,523,9355
1775072078,2115501,db-read-1,0.10,1,21
1775072078,2115502,db-read-2,0.50,5,26
1775072078,2115503,db-read-3,0.10,1,20
1775072078,2115504,db-read-4,0.10,1,19
1775072078,2115505,db-write,0.00,0,1
1775072078,2115506,event-worker,0.00,0,0
1775072088,2115498,lws-main,33.40,334,9689
1775072088,2115501,db-read-1,0.10,1,22
1775072088,2115502,db-read-2,0.30,3,29
1775072088,2115503,db-read-3,0.10,1,21
1775072088,2115504,db-read-4,0.10,1,20
1775072088,2115505,db-write,0.00,0,1
1775072088,2115506,event-worker,0.00,0,0
1775072098,2115498,lws-main,75.30,753,10442
1775072098,2115501,db-read-1,0.00,0,22
1775072098,2115502,db-read-2,0.00,0,29
1775072098,2115503,db-read-3,0.00,0,21
1775072098,2115504,db-read-4,0.00,0,20
1775072098,2115505,db-write,0.00,0,1
1775072098,2115506,event-worker,0.00,0,0
1775072108,2115498,lws-main,100.20,1002,11444
1775072108,2115501,db-read-1,0.00,0,22
1775072108,2115502,db-read-2,0.00,0,29
1775072108,2115503,db-read-3,0.10,1,22
1775072108,2115504,db-read-4,0.00,0,20
1775072108,2115505,db-write,0.00,0,1
1775072108,2115506,event-worker,0.00,0,0
1775072118,2115498,lws-main,78.40,784,12228
1775072118,2115501,db-read-1,0.00,0,22
1775072118,2115502,db-read-2,0.10,1,30
1775072118,2115503,db-read-3,0.00,0,22
1775072118,2115504,db-read-4,0.00,0,20
1775072118,2115505,db-write,0.00,0,1
1775072118,2115506,event-worker,0.00,0,0
1775072128,2115498,lws-main,27.80,278,12506
1775072128,2115501,db-read-1,0.00,0,22
1775072128,2115502,db-read-2,0.00,0,30
1775072128,2115503,db-read-3,0.00,0,22
1775072128,2115504,db-read-4,0.10,1,21
1775072128,2115505,db-write,0.00,0,1
1775072128,2115506,event-worker,0.00,0,0
1775072138,2115498,lws-main,65.80,658,13164
1775072138,2115501,db-read-1,0.40,4,26
1775072138,2115502,db-read-2,0.30,3,33
1775072138,2115503,db-read-3,0.20,2,24
1775072138,2115504,db-read-4,0.30,3,24
1775072138,2115505,db-write,0.00,0,1
1775072138,2115506,event-worker,0.00,0,0
1775072148,2115498,lws-main,30.70,307,13471
1775072148,2115501,db-read-1,0.00,0,26
1775072148,2115502,db-read-2,0.00,0,33
1775072148,2115503,db-read-3,0.00,0,24
1775072148,2115504,db-read-4,0.10,1,25
1775072148,2115505,db-write,0.00,0,1
1775072148,2115506,event-worker,0.00,0,0
1775072158,2115498,lws-main,68.80,688,14159
1775072158,2115501,db-read-1,0.40,4,30
1775072158,2115502,db-read-2,0.30,3,36
1775072158,2115503,db-read-3,0.10,1,25
1775072158,2115504,db-read-4,0.20,2,27
1775072158,2115505,db-write,0.00,0,1
1775072158,2115506,event-worker,0.00,0,0
1775072168,2115498,lws-main,38.20,382,14541
1775072168,2115501,db-read-1,0.00,0,30
1775072168,2115502,db-read-2,0.00,0,36
1775072168,2115503,db-read-3,0.00,0,25
1775072168,2115504,db-read-4,0.00,0,27
1775072168,2115505,db-write,0.00,0,1
1775072168,2115506,event-worker,0.00,0,0
1775072178,2115498,lws-main,52.20,522,15063
1775072178,2115501,db-read-1,0.10,1,31
1775072178,2115502,db-read-2,0.10,1,37
1775072178,2115503,db-read-3,0.20,2,27
1775072178,2115504,db-read-4,0.30,3,30
1775072178,2115505,db-write,0.00,0,1
1775072178,2115506,event-worker,0.00,0,0
1775072188,2115498,lws-main,78.30,783,15846
1775072188,2115501,db-read-1,0.10,1,32
1775072188,2115502,db-read-2,0.10,1,38
1775072188,2115503,db-read-3,0.00,0,27
1775072188,2115504,db-read-4,0.10,1,31
1775072188,2115505,db-write,0.00,0,1
1775072188,2115506,event-worker,0.00,0,0
1775072198,2115498,lws-main,96.90,969,16815
1775072198,2115501,db-read-1,0.10,1,33
1775072198,2115502,db-read-2,0.00,0,38
1775072198,2115503,db-read-3,0.10,1,28
1775072198,2115504,db-read-4,0.10,1,32
1775072198,2115505,db-write,0.00,0,1
1775072198,2115506,event-worker,0.00,0,0
1775072208,2115498,lws-main,87.80,878,17693
1775072208,2115501,db-read-1,0.10,1,34
1775072208,2115502,db-read-2,0.10,1,39
1775072208,2115503,db-read-3,0.10,1,29
1775072208,2115504,db-read-4,0.00,0,32
1775072208,2115505,db-write,0.00,0,1
1775072208,2115506,event-worker,0.00,0,0
1775072218,2115498,lws-main,77.20,772,18465
1775072218,2115501,db-read-1,0.00,0,34
1775072218,2115502,db-read-2,0.10,1,40
1775072218,2115503,db-read-3,0.10,1,30
1775072218,2115504,db-read-4,0.00,0,32
1775072218,2115505,db-write,0.00,0,1
1775072218,2115506,event-worker,0.00,0,0
1775072228,2115498,lws-main,21.70,217,18682
1775072228,2115501,db-read-1,0.00,0,34
1775072228,2115502,db-read-2,0.00,0,40
1775072228,2115503,db-read-3,0.00,0,30
1775072228,2115504,db-read-4,0.00,0,32
1775072228,2115505,db-write,0.00,0,1
1775072228,2115506,event-worker,0.00,0,0
1 timestamp tid thread_name cpu_pct delta_ticks total_ticks
2 1775071937 2115498 lws-main 0.00 0 2521
3 1775071937 2115501 db-read-1 0.00 0 9
4 1775071937 2115502 db-read-2 0.00 0 10
5 1775071937 2115503 db-read-3 0.00 0 9
6 1775071937 2115504 db-read-4 0.00 0 9
7 1775071937 2115505 db-write 0.00 0 0
8 1775071937 2115506 event-worker 0.00 0 0
9 1775071947 2115498 lws-main 0.10 1 2522
10 1775071947 2115501 db-read-1 0.10 1 10
11 1775071947 2115502 db-read-2 0.00 0 10
12 1775071947 2115503 db-read-3 0.00 0 9
13 1775071947 2115504 db-read-4 0.30 3 12
14 1775071947 2115505 db-write 0.00 0 0
15 1775071947 2115506 event-worker 0.00 0 0
16 1775071957 2115498 lws-main 26.80 268 2790
17 1775071957 2115501 db-read-1 0.00 0 10
18 1775071957 2115502 db-read-2 0.10 1 11
19 1775071957 2115503 db-read-3 0.00 0 9
20 1775071957 2115504 db-read-4 0.00 0 12
21 1775071957 2115505 db-write 0.00 0 0
22 1775071957 2115506 event-worker 0.00 0 0
23 1775071967 2115498 lws-main 72.10 721 3511
24 1775071967 2115501 db-read-1 0.00 0 10
25 1775071967 2115502 db-read-2 0.00 0 11
26 1775071967 2115503 db-read-3 0.00 0 9
27 1775071967 2115504 db-read-4 0.00 0 12
28 1775071967 2115505 db-write 0.00 0 0
29 1775071967 2115506 event-worker 0.00 0 0
30 1775071977 2115498 lws-main 64.40 644 4155
31 1775071977 2115501 db-read-1 0.20 2 12
32 1775071977 2115502 db-read-2 0.10 1 12
33 1775071977 2115503 db-read-3 0.10 1 10
34 1775071977 2115504 db-read-4 0.20 2 14
35 1775071977 2115505 db-write 0.00 0 0
36 1775071977 2115506 event-worker 0.00 0 0
37 1775071987 2115498 lws-main 42.60 426 4581
38 1775071987 2115501 db-read-1 0.10 1 13
39 1775071987 2115502 db-read-2 0.10 1 13
40 1775071987 2115503 db-read-3 0.10 1 11
41 1775071987 2115504 db-read-4 0.00 0 14
42 1775071987 2115505 db-write 0.00 0 0
43 1775071987 2115506 event-worker 0.00 0 0
44 1775071997 2115498 lws-main 7.20 72 4653
45 1775071997 2115501 db-read-1 0.00 0 13
46 1775071997 2115502 db-read-2 0.00 0 13
47 1775071997 2115503 db-read-3 0.00 0 11
48 1775071997 2115504 db-read-4 0.00 0 14
49 1775071997 2115505 db-write 0.00 0 0
50 1775071997 2115506 event-worker 0.00 0 0
51 1775072007 2115498 lws-main 42.90 429 5082
52 1775072007 2115501 db-read-1 0.00 0 13
53 1775072007 2115502 db-read-2 0.00 0 13
54 1775072007 2115503 db-read-3 0.00 0 11
55 1775072007 2115504 db-read-4 0.00 0 14
56 1775072007 2115505 db-write 0.00 0 0
57 1775072007 2115506 event-worker 0.00 0 0
58 1775072017 2115498 lws-main 38.10 381 5463
59 1775072017 2115501 db-read-1 0.10 1 14
60 1775072017 2115502 db-read-2 0.20 2 15
61 1775072017 2115503 db-read-3 0.40 4 15
62 1775072017 2115504 db-read-4 0.10 1 15
63 1775072017 2115505 db-write 0.00 0 0
64 1775072017 2115506 event-worker 0.00 0 0
65 1775072027 2115498 lws-main 93.20 932 6395
66 1775072027 2115501 db-read-1 0.40 4 18
67 1775072027 2115502 db-read-2 0.40 4 19
68 1775072027 2115503 db-read-3 0.00 0 15
69 1775072027 2115504 db-read-4 0.10 1 16
70 1775072027 2115505 db-write 0.00 0 0
71 1775072027 2115506 event-worker 0.00 0 0
72 1775072037 2115498 lws-main 95.10 951 7346
73 1775072037 2115501 db-read-1 0.10 1 19
74 1775072037 2115502 db-read-2 0.20 2 21
75 1775072037 2115503 db-read-3 0.30 3 18
76 1775072037 2115504 db-read-4 0.00 0 16
77 1775072037 2115505 db-write 0.10 1 1
78 1775072037 2115506 event-worker 0.00 0 0
79 1775072047 2115498 lws-main 65.00 650 7996
80 1775072047 2115501 db-read-1 0.00 0 19
81 1775072047 2115502 db-read-2 0.00 0 21
82 1775072047 2115503 db-read-3 0.10 1 19
83 1775072047 2115504 db-read-4 0.10 1 17
84 1775072047 2115505 db-write 0.00 0 1
85 1775072047 2115506 event-worker 0.00 0 0
86 1775072057 2115498 lws-main 62.80 628 8624
87 1775072057 2115501 db-read-1 0.00 0 19
88 1775072057 2115502 db-read-2 0.00 0 21
89 1775072057 2115503 db-read-3 0.00 0 19
90 1775072057 2115504 db-read-4 0.10 1 18
91 1775072057 2115505 db-write 0.00 0 1
92 1775072057 2115506 event-worker 0.00 0 0
93 1775072068 2115498 lws-main 20.80 208 8832
94 1775072068 2115501 db-read-1 0.10 1 20
95 1775072068 2115502 db-read-2 0.00 0 21
96 1775072068 2115503 db-read-3 0.00 0 19
97 1775072068 2115504 db-read-4 0.00 0 18
98 1775072068 2115505 db-write 0.00 0 1
99 1775072068 2115506 event-worker 0.00 0 0
100 1775072078 2115498 lws-main 52.30 523 9355
101 1775072078 2115501 db-read-1 0.10 1 21
102 1775072078 2115502 db-read-2 0.50 5 26
103 1775072078 2115503 db-read-3 0.10 1 20
104 1775072078 2115504 db-read-4 0.10 1 19
105 1775072078 2115505 db-write 0.00 0 1
106 1775072078 2115506 event-worker 0.00 0 0
107 1775072088 2115498 lws-main 33.40 334 9689
108 1775072088 2115501 db-read-1 0.10 1 22
109 1775072088 2115502 db-read-2 0.30 3 29
110 1775072088 2115503 db-read-3 0.10 1 21
111 1775072088 2115504 db-read-4 0.10 1 20
112 1775072088 2115505 db-write 0.00 0 1
113 1775072088 2115506 event-worker 0.00 0 0
114 1775072098 2115498 lws-main 75.30 753 10442
115 1775072098 2115501 db-read-1 0.00 0 22
116 1775072098 2115502 db-read-2 0.00 0 29
117 1775072098 2115503 db-read-3 0.00 0 21
118 1775072098 2115504 db-read-4 0.00 0 20
119 1775072098 2115505 db-write 0.00 0 1
120 1775072098 2115506 event-worker 0.00 0 0
121 1775072108 2115498 lws-main 100.20 1002 11444
122 1775072108 2115501 db-read-1 0.00 0 22
123 1775072108 2115502 db-read-2 0.00 0 29
124 1775072108 2115503 db-read-3 0.10 1 22
125 1775072108 2115504 db-read-4 0.00 0 20
126 1775072108 2115505 db-write 0.00 0 1
127 1775072108 2115506 event-worker 0.00 0 0
128 1775072118 2115498 lws-main 78.40 784 12228
129 1775072118 2115501 db-read-1 0.00 0 22
130 1775072118 2115502 db-read-2 0.10 1 30
131 1775072118 2115503 db-read-3 0.00 0 22
132 1775072118 2115504 db-read-4 0.00 0 20
133 1775072118 2115505 db-write 0.00 0 1
134 1775072118 2115506 event-worker 0.00 0 0
135 1775072128 2115498 lws-main 27.80 278 12506
136 1775072128 2115501 db-read-1 0.00 0 22
137 1775072128 2115502 db-read-2 0.00 0 30
138 1775072128 2115503 db-read-3 0.00 0 22
139 1775072128 2115504 db-read-4 0.10 1 21
140 1775072128 2115505 db-write 0.00 0 1
141 1775072128 2115506 event-worker 0.00 0 0
142 1775072138 2115498 lws-main 65.80 658 13164
143 1775072138 2115501 db-read-1 0.40 4 26
144 1775072138 2115502 db-read-2 0.30 3 33
145 1775072138 2115503 db-read-3 0.20 2 24
146 1775072138 2115504 db-read-4 0.30 3 24
147 1775072138 2115505 db-write 0.00 0 1
148 1775072138 2115506 event-worker 0.00 0 0
149 1775072148 2115498 lws-main 30.70 307 13471
150 1775072148 2115501 db-read-1 0.00 0 26
151 1775072148 2115502 db-read-2 0.00 0 33
152 1775072148 2115503 db-read-3 0.00 0 24
153 1775072148 2115504 db-read-4 0.10 1 25
154 1775072148 2115505 db-write 0.00 0 1
155 1775072148 2115506 event-worker 0.00 0 0
156 1775072158 2115498 lws-main 68.80 688 14159
157 1775072158 2115501 db-read-1 0.40 4 30
158 1775072158 2115502 db-read-2 0.30 3 36
159 1775072158 2115503 db-read-3 0.10 1 25
160 1775072158 2115504 db-read-4 0.20 2 27
161 1775072158 2115505 db-write 0.00 0 1
162 1775072158 2115506 event-worker 0.00 0 0
163 1775072168 2115498 lws-main 38.20 382 14541
164 1775072168 2115501 db-read-1 0.00 0 30
165 1775072168 2115502 db-read-2 0.00 0 36
166 1775072168 2115503 db-read-3 0.00 0 25
167 1775072168 2115504 db-read-4 0.00 0 27
168 1775072168 2115505 db-write 0.00 0 1
169 1775072168 2115506 event-worker 0.00 0 0
170 1775072178 2115498 lws-main 52.20 522 15063
171 1775072178 2115501 db-read-1 0.10 1 31
172 1775072178 2115502 db-read-2 0.10 1 37
173 1775072178 2115503 db-read-3 0.20 2 27
174 1775072178 2115504 db-read-4 0.30 3 30
175 1775072178 2115505 db-write 0.00 0 1
176 1775072178 2115506 event-worker 0.00 0 0
177 1775072188 2115498 lws-main 78.30 783 15846
178 1775072188 2115501 db-read-1 0.10 1 32
179 1775072188 2115502 db-read-2 0.10 1 38
180 1775072188 2115503 db-read-3 0.00 0 27
181 1775072188 2115504 db-read-4 0.10 1 31
182 1775072188 2115505 db-write 0.00 0 1
183 1775072188 2115506 event-worker 0.00 0 0
184 1775072198 2115498 lws-main 96.90 969 16815
185 1775072198 2115501 db-read-1 0.10 1 33
186 1775072198 2115502 db-read-2 0.00 0 38
187 1775072198 2115503 db-read-3 0.10 1 28
188 1775072198 2115504 db-read-4 0.10 1 32
189 1775072198 2115505 db-write 0.00 0 1
190 1775072198 2115506 event-worker 0.00 0 0
191 1775072208 2115498 lws-main 87.80 878 17693
192 1775072208 2115501 db-read-1 0.10 1 34
193 1775072208 2115502 db-read-2 0.10 1 39
194 1775072208 2115503 db-read-3 0.10 1 29
195 1775072208 2115504 db-read-4 0.00 0 32
196 1775072208 2115505 db-write 0.00 0 1
197 1775072208 2115506 event-worker 0.00 0 0
198 1775072218 2115498 lws-main 77.20 772 18465
199 1775072218 2115501 db-read-1 0.00 0 34
200 1775072218 2115502 db-read-2 0.10 1 40
201 1775072218 2115503 db-read-3 0.10 1 30
202 1775072218 2115504 db-read-4 0.00 0 32
203 1775072218 2115505 db-write 0.00 0 1
204 1775072218 2115506 event-worker 0.00 0 0
205 1775072228 2115498 lws-main 21.70 217 18682
206 1775072228 2115501 db-read-1 0.00 0 34
207 1775072228 2115502 db-read-2 0.00 0 40
208 1775072228 2115503 db-read-3 0.00 0 30
209 1775072228 2115504 db-read-4 0.00 0 32
210 1775072228 2115505 db-write 0.00 0 1
211 1775072228 2115506 event-worker 0.00 0 0
@@ -0,0 +1,14 @@
==========================================
Thread CPU Summary (avg + max over run)
==========================================
Run timestamp (UTC): 20260401_193216
Duration: 300s, Interval: 10s
2115498 lws-main 53.87 100.20
2115502 db-read-2 0.10 0.50
2115501 db-read-1 0.08 0.40
2115504 db-read-4 0.08 0.30
2115503 db-read-3 0.07 0.40
2115505 db-write 0.00 0.10
2115506 event-worker 0.00 0.00
TID THREAD AVG_CPU% MAX_CPU%
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
Profiling PID: 2130230
CLK_TCK=100
Starting perf record in background...
perf_pid=2131008
Done. Artifacts in /tmp/c_relay_thread_profile_20260401_202647
@@ -0,0 +1,121 @@
timestamp,tid,thread_name,cpu_pct,delta_ticks,total_ticks
1775075209,2130230,lws-main,0.00,0,17777
1775075209,2130232,db-read-1,0.00,0,84
1775075209,2130233,db-write,0.00,0,15
1775075209,2130234,event-worker,0.00,0,1
1775075219,2130230,lws-main,21.50,215,17992
1775075219,2130232,db-read-1,0.80,8,92
1775075219,2130233,db-write,0.00,0,15
1775075219,2130234,event-worker,0.00,0,1
1775075229,2130230,lws-main,50.40,504,18496
1775075229,2130232,db-read-1,0.70,7,99
1775075229,2130233,db-write,0.00,0,15
1775075229,2130234,event-worker,0.00,0,1
1775075239,2130230,lws-main,90.10,901,19397
1775075239,2130232,db-read-1,0.30,3,102
1775075239,2130233,db-write,0.00,0,15
1775075239,2130234,event-worker,0.00,0,1
1775075249,2130230,lws-main,100.10,1001,20398
1775075249,2130232,db-read-1,0.00,0,102
1775075249,2130233,db-write,0.00,0,15
1775075249,2130234,event-worker,0.00,0,1
1775075259,2130230,lws-main,99.40,994,21392
1775075259,2130232,db-read-1,0.50,5,107
1775075259,2130233,db-write,0.00,0,15
1775075259,2130234,event-worker,0.00,0,1
1775075269,2130230,lws-main,78.00,780,22172
1775075269,2130232,db-read-1,0.60,6,113
1775075269,2130233,db-write,0.00,0,15
1775075269,2130234,event-worker,0.00,0,1
1775075279,2130230,lws-main,14.40,144,22316
1775075279,2130232,db-read-1,0.20,2,115
1775075279,2130233,db-write,0.00,0,15
1775075279,2130234,event-worker,0.00,0,1
1775075289,2130230,lws-main,49.60,496,22812
1775075289,2130232,db-read-1,0.60,6,121
1775075289,2130233,db-write,0.00,0,15
1775075289,2130234,event-worker,0.00,0,1
1775075299,2130230,lws-main,91.70,917,23729
1775075299,2130232,db-read-1,0.20,2,123
1775075299,2130233,db-write,0.00,0,15
1775075299,2130234,event-worker,0.00,0,1
1775075309,2130230,lws-main,77.90,779,24508
1775075309,2130232,db-read-1,0.60,6,129
1775075309,2130233,db-write,0.00,0,15
1775075309,2130234,event-worker,0.00,0,1
1775075319,2130230,lws-main,87.40,874,25382
1775075319,2130232,db-read-1,0.20,2,131
1775075319,2130233,db-write,0.00,0,15
1775075319,2130234,event-worker,0.00,0,1
1775075329,2130230,lws-main,67.40,674,26056
1775075329,2130232,db-read-1,0.10,1,132
1775075329,2130233,db-write,0.00,0,15
1775075329,2130234,event-worker,0.00,0,1
1775075339,2130230,lws-main,100.20,1002,27058
1775075339,2130232,db-read-1,0.00,0,132
1775075339,2130233,db-write,0.00,0,15
1775075339,2130234,event-worker,0.00,0,1
1775075349,2130230,lws-main,100.00,1000,28058
1775075349,2130232,db-read-1,0.10,1,133
1775075349,2130233,db-write,0.00,0,15
1775075349,2130234,event-worker,0.00,0,1
1775075359,2130230,lws-main,100.10,1001,29059
1775075359,2130232,db-read-1,0.10,1,134
1775075359,2130233,db-write,0.10,1,16
1775075359,2130234,event-worker,0.00,0,1
1775075369,2130230,lws-main,100.10,1001,30060
1775075369,2130232,db-read-1,0.20,2,136
1775075369,2130233,db-write,0.00,0,16
1775075369,2130234,event-worker,0.00,0,1
1775075379,2130230,lws-main,18.30,183,30243
1775075379,2130232,db-read-1,0.40,4,140
1775075379,2130233,db-write,0.00,0,16
1775075379,2130234,event-worker,0.10,1,2
1775075389,2130230,lws-main,67.40,674,30917
1775075389,2130232,db-read-1,0.10,1,141
1775075389,2130233,db-write,0.10,1,17
1775075389,2130234,event-worker,0.00,0,2
1775075399,2130230,lws-main,86.00,860,31777
1775075399,2130232,db-read-1,0.10,1,142
1775075399,2130233,db-write,0.00,0,17
1775075399,2130234,event-worker,0.00,0,2
1775075409,2130230,lws-main,72.00,720,32497
1775075409,2130232,db-read-1,0.50,5,147
1775075409,2130233,db-write,0.00,0,17
1775075409,2130234,event-worker,0.00,0,2
1775075419,2130230,lws-main,21.40,214,32711
1775075419,2130232,db-read-1,0.10,1,148
1775075419,2130233,db-write,0.00,0,17
1775075419,2130234,event-worker,0.00,0,2
1775075429,2130230,lws-main,43.20,432,33143
1775075429,2130232,db-read-1,0.00,0,148
1775075429,2130233,db-write,0.00,0,17
1775075429,2130234,event-worker,0.00,0,2
1775075439,2130230,lws-main,15.40,154,33297
1775075439,2130232,db-read-1,0.00,0,148
1775075439,2130233,db-write,0.30,3,20
1775075439,2130234,event-worker,0.00,0,2
1775075449,2130230,lws-main,50.80,508,33805
1775075449,2130232,db-read-1,0.50,5,153
1775075449,2130233,db-write,0.00,0,20
1775075449,2130234,event-worker,0.00,0,2
1775075459,2130230,lws-main,37.00,370,34175
1775075459,2130232,db-read-1,0.20,2,155
1775075459,2130233,db-write,0.00,0,20
1775075459,2130234,event-worker,0.00,0,2
1775075469,2130230,lws-main,89.20,892,35067
1775075469,2130232,db-read-1,0.20,2,157
1775075469,2130233,db-write,0.00,0,20
1775075469,2130234,event-worker,0.00,0,2
1775075479,2130230,lws-main,99.20,992,36059
1775075479,2130232,db-read-1,0.60,6,163
1775075479,2130233,db-write,0.10,1,21
1775075479,2130234,event-worker,0.10,1,3
1775075489,2130230,lws-main,100.00,1000,37059
1775075489,2130232,db-read-1,0.10,1,164
1775075489,2130233,db-write,0.00,0,21
1775075489,2130234,event-worker,0.00,0,3
1775075499,2130230,lws-main,99.70,997,38056
1775075499,2130232,db-read-1,0.20,2,166
1775075499,2130233,db-write,0.00,0,21
1775075499,2130234,event-worker,0.10,1,4
1 timestamp tid thread_name cpu_pct delta_ticks total_ticks
2 1775075209 2130230 lws-main 0.00 0 17777
3 1775075209 2130232 db-read-1 0.00 0 84
4 1775075209 2130233 db-write 0.00 0 15
5 1775075209 2130234 event-worker 0.00 0 1
6 1775075219 2130230 lws-main 21.50 215 17992
7 1775075219 2130232 db-read-1 0.80 8 92
8 1775075219 2130233 db-write 0.00 0 15
9 1775075219 2130234 event-worker 0.00 0 1
10 1775075229 2130230 lws-main 50.40 504 18496
11 1775075229 2130232 db-read-1 0.70 7 99
12 1775075229 2130233 db-write 0.00 0 15
13 1775075229 2130234 event-worker 0.00 0 1
14 1775075239 2130230 lws-main 90.10 901 19397
15 1775075239 2130232 db-read-1 0.30 3 102
16 1775075239 2130233 db-write 0.00 0 15
17 1775075239 2130234 event-worker 0.00 0 1
18 1775075249 2130230 lws-main 100.10 1001 20398
19 1775075249 2130232 db-read-1 0.00 0 102
20 1775075249 2130233 db-write 0.00 0 15
21 1775075249 2130234 event-worker 0.00 0 1
22 1775075259 2130230 lws-main 99.40 994 21392
23 1775075259 2130232 db-read-1 0.50 5 107
24 1775075259 2130233 db-write 0.00 0 15
25 1775075259 2130234 event-worker 0.00 0 1
26 1775075269 2130230 lws-main 78.00 780 22172
27 1775075269 2130232 db-read-1 0.60 6 113
28 1775075269 2130233 db-write 0.00 0 15
29 1775075269 2130234 event-worker 0.00 0 1
30 1775075279 2130230 lws-main 14.40 144 22316
31 1775075279 2130232 db-read-1 0.20 2 115
32 1775075279 2130233 db-write 0.00 0 15
33 1775075279 2130234 event-worker 0.00 0 1
34 1775075289 2130230 lws-main 49.60 496 22812
35 1775075289 2130232 db-read-1 0.60 6 121
36 1775075289 2130233 db-write 0.00 0 15
37 1775075289 2130234 event-worker 0.00 0 1
38 1775075299 2130230 lws-main 91.70 917 23729
39 1775075299 2130232 db-read-1 0.20 2 123
40 1775075299 2130233 db-write 0.00 0 15
41 1775075299 2130234 event-worker 0.00 0 1
42 1775075309 2130230 lws-main 77.90 779 24508
43 1775075309 2130232 db-read-1 0.60 6 129
44 1775075309 2130233 db-write 0.00 0 15
45 1775075309 2130234 event-worker 0.00 0 1
46 1775075319 2130230 lws-main 87.40 874 25382
47 1775075319 2130232 db-read-1 0.20 2 131
48 1775075319 2130233 db-write 0.00 0 15
49 1775075319 2130234 event-worker 0.00 0 1
50 1775075329 2130230 lws-main 67.40 674 26056
51 1775075329 2130232 db-read-1 0.10 1 132
52 1775075329 2130233 db-write 0.00 0 15
53 1775075329 2130234 event-worker 0.00 0 1
54 1775075339 2130230 lws-main 100.20 1002 27058
55 1775075339 2130232 db-read-1 0.00 0 132
56 1775075339 2130233 db-write 0.00 0 15
57 1775075339 2130234 event-worker 0.00 0 1
58 1775075349 2130230 lws-main 100.00 1000 28058
59 1775075349 2130232 db-read-1 0.10 1 133
60 1775075349 2130233 db-write 0.00 0 15
61 1775075349 2130234 event-worker 0.00 0 1
62 1775075359 2130230 lws-main 100.10 1001 29059
63 1775075359 2130232 db-read-1 0.10 1 134
64 1775075359 2130233 db-write 0.10 1 16
65 1775075359 2130234 event-worker 0.00 0 1
66 1775075369 2130230 lws-main 100.10 1001 30060
67 1775075369 2130232 db-read-1 0.20 2 136
68 1775075369 2130233 db-write 0.00 0 16
69 1775075369 2130234 event-worker 0.00 0 1
70 1775075379 2130230 lws-main 18.30 183 30243
71 1775075379 2130232 db-read-1 0.40 4 140
72 1775075379 2130233 db-write 0.00 0 16
73 1775075379 2130234 event-worker 0.10 1 2
74 1775075389 2130230 lws-main 67.40 674 30917
75 1775075389 2130232 db-read-1 0.10 1 141
76 1775075389 2130233 db-write 0.10 1 17
77 1775075389 2130234 event-worker 0.00 0 2
78 1775075399 2130230 lws-main 86.00 860 31777
79 1775075399 2130232 db-read-1 0.10 1 142
80 1775075399 2130233 db-write 0.00 0 17
81 1775075399 2130234 event-worker 0.00 0 2
82 1775075409 2130230 lws-main 72.00 720 32497
83 1775075409 2130232 db-read-1 0.50 5 147
84 1775075409 2130233 db-write 0.00 0 17
85 1775075409 2130234 event-worker 0.00 0 2
86 1775075419 2130230 lws-main 21.40 214 32711
87 1775075419 2130232 db-read-1 0.10 1 148
88 1775075419 2130233 db-write 0.00 0 17
89 1775075419 2130234 event-worker 0.00 0 2
90 1775075429 2130230 lws-main 43.20 432 33143
91 1775075429 2130232 db-read-1 0.00 0 148
92 1775075429 2130233 db-write 0.00 0 17
93 1775075429 2130234 event-worker 0.00 0 2
94 1775075439 2130230 lws-main 15.40 154 33297
95 1775075439 2130232 db-read-1 0.00 0 148
96 1775075439 2130233 db-write 0.30 3 20
97 1775075439 2130234 event-worker 0.00 0 2
98 1775075449 2130230 lws-main 50.80 508 33805
99 1775075449 2130232 db-read-1 0.50 5 153
100 1775075449 2130233 db-write 0.00 0 20
101 1775075449 2130234 event-worker 0.00 0 2
102 1775075459 2130230 lws-main 37.00 370 34175
103 1775075459 2130232 db-read-1 0.20 2 155
104 1775075459 2130233 db-write 0.00 0 20
105 1775075459 2130234 event-worker 0.00 0 2
106 1775075469 2130230 lws-main 89.20 892 35067
107 1775075469 2130232 db-read-1 0.20 2 157
108 1775075469 2130233 db-write 0.00 0 20
109 1775075469 2130234 event-worker 0.00 0 2
110 1775075479 2130230 lws-main 99.20 992 36059
111 1775075479 2130232 db-read-1 0.60 6 163
112 1775075479 2130233 db-write 0.10 1 21
113 1775075479 2130234 event-worker 0.10 1 3
114 1775075489 2130230 lws-main 100.00 1000 37059
115 1775075489 2130232 db-read-1 0.10 1 164
116 1775075489 2130233 db-write 0.00 0 21
117 1775075489 2130234 event-worker 0.00 0 3
118 1775075499 2130230 lws-main 99.70 997 38056
119 1775075499 2130232 db-read-1 0.20 2 166
120 1775075499 2130233 db-write 0.00 0 21
121 1775075499 2130234 event-worker 0.10 1 4
@@ -0,0 +1,11 @@
==========================================
Thread CPU Summary (avg + max over run)
==========================================
Run timestamp (UTC): 20260401_202647
Duration: 300s, Interval: 10s
2130230 lws-main 67.60 100.20
2130232 db-read-1 0.27 0.80
2130233 db-write 0.02 0.30
2130234 event-worker 0.01 0.10
TID THREAD AVG_CPU% MAX_CPU%