Compare commits

...
13 Commits
Author SHA1 Message Date
Your Name ad99f74bc0 v0.0.40 - Reduce query churn and add relay disconnect-cause visibility 2026-03-05 15:18:16 -04:00
Your Name cf9c300fdf v0.0.39 - Rebuild didactyl with ws frame drain and larger relay buffers 2026-03-05 14:59:53 -04:00
Your Name 97585890b9 v0.0.38 - Forward non-matching query_sync messages to pool dispatch to prevent DM loss during sync queries 2026-03-05 14:13:26 -04:00
Your Name 7b3d36a797 dm subscriptions: keep kind4/kind1059 subs open after EOSE 2026-03-05 11:56:42 -04:00
Your Name 49579b17a4 dm send: remove nested poll drain and auth retry path 2026-03-05 11:29:50 -04:00
Your Name 17083de47d v0.0.37 - Add relay-pool publish connection-skip and poll latency instrumentation for hang diagnosis 2026-03-05 11:16:35 -04:00
Your Name 33884046af debug: add poll latency instrumentation 2026-03-05 11:15:17 -04:00
Your Name 7e69819be5 v0.0.36 - Remove inline NIP-17 poll+retry window to prevent post-reply hang while keeping send-path debug logging 2026-03-05 09:58:06 -04:00
Your Name d189d0ba8c v0.0.35 - Improve NIP-17 send reliability with re-entrancy guard, auth handshake retry window, and send-path debug logs 2026-03-05 09:50:48 -04:00
Your Name 2cda3b6a58 v0.0.34 - Fix NIP-17 send hang by removing re-entrant relay query and nested polling; add startup kind 10050 relay list 2026-03-05 09:36:26 -04:00
Your Name ecb58b7e11 v0.0.33 - Skip self-sent NIP-17 sender-copy DMs to prevent self-reply loops 2026-03-05 09:17:54 -04:00
Your Name c3645e6af5 v0.0.32 - Prevent NIP-17 old message reprocessing by guarding rumor created_at against startup time 2026-03-05 07:55:41 -04:00
Your Name 6cc46d2c25 v0.0.31 - Fix NIP-17 DM receive by using separate kind4/kind1059 subscriptions and 2-day lookback for gift wraps 2026-03-05 06:32:14 -04:00
7 changed files with 2928 additions and 7960 deletions
+2 -2
View File
@@ -51,11 +51,11 @@ Agents learn capabilities through skills — Nostr events that any agent can di
Didactyl will support local inference, which is very privacy preserving. Remote inference does however have it's advantages, and in those cases Didactyl supports using Bitcoin Lightning and eCash inference providers.
## Current Status — v0.0.30
## Current Status — v0.0.40
**Active build — this project is barely working. Experiment at your own risk.**
> Last release update: v0.0.30 — Add config-controlled dm_protocol (nip04/nip17/both), NIP-17 receive handling, protocol-aware auto DM routing, and config updates
> Last release update: v0.0.40 — Reduce query churn and add relay disconnect-cause visibility
- Connects to configured relays with auto-reconnect and relay state transition logging
- Publishes configured startup events per relay as each relay becomes connected
+2711 -7775
View File
File diff suppressed because one or more lines are too long
+4 -4
View File
@@ -35,7 +35,7 @@ static pthread_mutex_t g_context_part_names_mutex = PTHREAD_MUTEX_INITIALIZER;
#define AGENT_DEBOUNCE_CACHE_SIZE 256
#define AGENT_HISTORY_TURNS 12
#define AGENT_HISTORY_QUERY_LIMIT 200
#define AGENT_ADOPTED_SKILLS_CACHE_TTL_SECONDS 30
#define AGENT_ADOPTED_SKILLS_CACHE_TTL_SECONDS 300
#define AGENT_ADOPTED_SKILLS_MAX 24
#define AGENT_ADOPTED_SKILL_CONTENT_MAX_CHARS 1800
#define AGENT_ADOPTED_SKILLS_TOTAL_MAX_CHARS 7200
@@ -1415,7 +1415,7 @@ static int append_recent_admin_dm_history(cJSON* messages, const char* current_m
cJSON_AddItemToObject(filter, "authors", authors);
cJSON_AddNumberToObject(filter, "limit", AGENT_HISTORY_QUERY_LIMIT);
char* events_json = nostr_handler_query_json(filter, 8000);
char* events_json = nostr_handler_query_json(filter, 1500);
cJSON_Delete(filter);
if (!events_json) {
return 0;
@@ -1648,7 +1648,7 @@ static int refresh_adopted_skills_cache_if_needed(void) {
cJSON_AddItemToObject(adopt_filter, "authors", authors);
cJSON_AddNumberToObject(adopt_filter, "limit", 1);
char* adoption_json = nostr_handler_query_json(adopt_filter, 8000);
char* adoption_json = nostr_handler_query_json(adopt_filter, 2000);
cJSON_Delete(adopt_filter);
cJSON* adoption_events = adoption_json ? cJSON_Parse(adoption_json) : NULL;
@@ -1700,7 +1700,7 @@ static int refresh_adopted_skills_cache_if_needed(void) {
cJSON_AddItemToObject(skill_filter, "#d", d_values);
cJSON_AddNumberToObject(skill_filter, "limit", 1);
char* skill_json = nostr_handler_query_json(skill_filter, 8000);
char* skill_json = nostr_handler_query_json(skill_filter, 2000);
cJSON_Delete(skill_filter);
if (!skill_json) {
continue;
+10 -1
View File
@@ -245,10 +245,19 @@ int main(int argc, char** argv) {
char startup_dm[768];
const char* startup_name = nostr_handler_get_startup_display_name();
int connected_relays = nostr_handler_connected_relay_count();
time_t startup_now = time(NULL);
struct tm startup_tm;
char startup_time_str[32] = {0};
if (localtime_r(&startup_now, &startup_tm)) {
(void)strftime(startup_time_str, sizeof(startup_time_str), "%Y-%m-%d %H:%M:%S", &startup_tm);
} else {
snprintf(startup_time_str, sizeof(startup_time_str), "%ld", (long)startup_now);
}
snprintf(startup_dm,
sizeof(startup_dm),
"%s has started up and is online (version %s, connected relays: %d/%d).",
"%s has started up and is online at %s (version %s, connected relays: %d/%d).",
(startup_name && startup_name[0] != '\0') ? startup_name : "Didactyl",
startup_time_str,
DIDACTYL_VERSION,
connected_relays,
cfg.relay_count);
+2 -2
View File
@@ -12,8 +12,8 @@
// Using DIDACTYL_ prefix to avoid conflicts with nostr_core_lib VERSION macros
#define DIDACTYL_VERSION_MAJOR 0
#define DIDACTYL_VERSION_MINOR 0
#define DIDACTYL_VERSION_PATCH 30
#define DIDACTYL_VERSION "v0.0.30"
#define DIDACTYL_VERSION_PATCH 40
#define DIDACTYL_VERSION "v0.0.40"
// Agent metadata
#define DIDACTYL_NAME "Didactyl"
+195 -172
View File
@@ -218,10 +218,19 @@ static void log_relay_state_changes(void) {
nostr_pool_relay_status_t now = nostr_relay_pool_get_relay_status(g_pool, relay);
nostr_pool_relay_status_t prev = g_last_relay_statuses[i];
if (now != prev) {
DEBUG_INFO("[didactyl] relay state changed: %s %s -> %s",
relay,
relay_status_str(prev),
relay_status_str(now));
const char* conn_err = nostr_relay_pool_get_relay_last_connection_error(g_pool, relay);
if (conn_err && (now == NOSTR_POOL_RELAY_DISCONNECTED || now == NOSTR_POOL_RELAY_ERROR)) {
DEBUG_INFO("[didactyl] relay state changed: %s %s -> %s (cause: %s)",
relay,
relay_status_str(prev),
relay_status_str(now),
conn_err);
} else {
DEBUG_INFO("[didactyl] relay state changed: %s %s -> %s",
relay,
relay_status_str(prev),
relay_status_str(now));
}
g_last_relay_statuses[i] = now;
if (now == NOSTR_POOL_RELAY_CONNECTED) {
@@ -628,14 +637,25 @@ static void on_event(cJSON* event, const char* relay_url, void* user_data) {
cJSON* rumor_kind = cJSON_GetObjectItemCaseSensitive(rumor, "kind");
cJSON* rumor_pubkey = cJSON_GetObjectItemCaseSensitive(rumor, "pubkey");
cJSON* rumor_content = cJSON_GetObjectItemCaseSensitive(rumor, "content");
cJSON* rumor_created_at = cJSON_GetObjectItemCaseSensitive(rumor, "created_at");
if (!rumor_kind || !rumor_pubkey || !rumor_content ||
if (!rumor_kind || !rumor_pubkey || !rumor_content || !rumor_created_at ||
!cJSON_IsNumber(rumor_kind) || !cJSON_IsString(rumor_pubkey) || !cJSON_IsString(rumor_content) ||
!cJSON_IsNumber(rumor_created_at) ||
!rumor_pubkey->valuestring || strlen(rumor_pubkey->valuestring) != 64U) {
cJSON_Delete(rumor);
return;
}
time_t rumor_created_at_ts = (time_t)rumor_created_at->valuedouble;
if (rumor_created_at_ts < g_start_time) {
DEBUG_TRACE("[didactyl] DEBUG on_event: skipping old NIP-17 rumor created_at=%ld start=%ld",
(long)rumor_created_at_ts,
(long)g_start_time);
cJSON_Delete(rumor);
return;
}
int rumor_kind_val = (int)rumor_kind->valuedouble;
if (rumor_kind_val != 14 && rumor_kind_val != 15 && rumor_kind_val != 7) {
DEBUG_TRACE("[didactyl] DEBUG on_event: ignoring NIP-17 rumor kind=%d", rumor_kind_val);
@@ -670,6 +690,14 @@ static void on_event(cJSON* event, const char* relay_url, void* user_data) {
return;
}
if (strcmp(sender_pubkey_hex, g_cfg->keys.public_key_hex) == 0) {
DEBUG_TRACE("[didactyl] DEBUG on_event: ignoring self-sent DM %.16s... via %s",
sender_pubkey_hex,
relay_url ? relay_url : "unknown relay");
free(decrypted);
return;
}
didactyl_sender_tier_t tier = sender_tier_from_pubkey(sender_pubkey_hex);
DEBUG_TRACE("[didactyl] DEBUG on_event: sender=%.16s... tier=%d (admin=%.16s...)",
@@ -1077,58 +1105,122 @@ int nostr_handler_subscribe_dms(dm_callback_t callback, void* user_data) {
g_dm_callback = callback;
g_dm_user_data = user_data;
cJSON* filter = cJSON_CreateObject();
cJSON* kinds = cJSON_CreateArray();
cJSON* p_values = cJSON_CreateArray();
if (!filter || !kinds || !p_values) {
cJSON_Delete(filter);
cJSON_Delete(kinds);
cJSON_Delete(p_values);
const int need_kind4 = (g_cfg->dm_protocol == DM_PROTOCOL_NIP04 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) ? 1 : 0;
const int need_kind1059 = (g_cfg->dm_protocol == DM_PROTOCOL_NIP17 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) ? 1 : 0;
if (!need_kind4 && !need_kind1059) {
DEBUG_WARN("[didactyl] DM subscription skipped: no protocol selected");
return -1;
}
if (g_cfg->dm_protocol == DM_PROTOCOL_NIP04 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) {
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(4));
}
if (g_cfg->dm_protocol == DM_PROTOCOL_NIP17 || g_cfg->dm_protocol == DM_PROTOCOL_BOTH) {
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(1059));
}
cJSON_AddItemToObject(filter, "kinds", kinds);
cJSON_AddItemToArray(p_values, cJSON_CreateString(g_cfg->keys.public_key_hex));
cJSON_AddItemToObject(filter, "#p", p_values);
cJSON_AddNumberToObject(filter, "since", (double)g_start_time);
cJSON_AddNumberToObject(filter, "limit", 100);
int subscribed_any = 0;
{
char* filter_str = cJSON_PrintUnformatted(filter);
DEBUG_TRACE("[didactyl] DEBUG DM subscription filter: %s", filter_str ? filter_str : "<null>");
DEBUG_TRACE("[didactyl] DEBUG DM subscription g_start_time=%ld now=%ld delta=%ld relay_count=%d",
(long)g_start_time, (long)time(NULL), (long)(time(NULL) - g_start_time), g_cfg->relay_count);
free(filter_str);
if (need_kind4) {
cJSON* filter4 = cJSON_CreateObject();
cJSON* kinds4 = cJSON_CreateArray();
cJSON* p_values4 = cJSON_CreateArray();
if (!filter4 || !kinds4 || !p_values4) {
cJSON_Delete(filter4);
cJSON_Delete(kinds4);
cJSON_Delete(p_values4);
return -1;
}
cJSON_AddItemToArray(kinds4, cJSON_CreateNumber(4));
cJSON_AddItemToObject(filter4, "kinds", kinds4);
cJSON_AddItemToArray(p_values4, cJSON_CreateString(g_cfg->keys.public_key_hex));
cJSON_AddItemToObject(filter4, "#p", p_values4);
cJSON_AddNumberToObject(filter4, "since", (double)g_start_time);
cJSON_AddNumberToObject(filter4, "limit", 100);
{
char* filter_str = cJSON_PrintUnformatted(filter4);
DEBUG_TRACE("[didactyl] DEBUG DM subscription filter kind4: %s", filter_str ? filter_str : "<null>");
free(filter_str);
}
nostr_pool_subscription_t* sub4 = nostr_relay_pool_subscribe(
g_pool,
(const char**)g_cfg->relays,
g_cfg->relay_count,
filter4,
on_event,
on_eose,
NULL,
0,
0,
NOSTR_POOL_EOSE_FULL_SET,
30,
120);
cJSON_Delete(filter4);
if (!sub4) {
fprintf(stderr, "[didactyl] kind4 DM subscription failed\n");
return -1;
}
subscribed_any = 1;
DEBUG_TRACE("[didactyl] DEBUG kind4 DM subscription sub=%p", (void*)sub4);
}
nostr_pool_subscription_t* sub = nostr_relay_pool_subscribe(
g_pool,
(const char**)g_cfg->relays,
g_cfg->relay_count,
filter,
on_event,
on_eose,
NULL,
0,
1,
NOSTR_POOL_EOSE_FULL_SET,
30,
120);
if (need_kind1059) {
time_t since_1059 = g_start_time;
cJSON_Delete(filter);
if (!sub) {
fprintf(stderr, "[didactyl] DM subscription failed\n");
cJSON* filter1059 = cJSON_CreateObject();
cJSON* kinds1059 = cJSON_CreateArray();
cJSON* p_values1059 = cJSON_CreateArray();
if (!filter1059 || !kinds1059 || !p_values1059) {
cJSON_Delete(filter1059);
cJSON_Delete(kinds1059);
cJSON_Delete(p_values1059);
return -1;
}
cJSON_AddItemToArray(kinds1059, cJSON_CreateNumber(1059));
cJSON_AddItemToObject(filter1059, "kinds", kinds1059);
cJSON_AddItemToArray(p_values1059, cJSON_CreateString(g_cfg->keys.public_key_hex));
cJSON_AddItemToObject(filter1059, "#p", p_values1059);
cJSON_AddNumberToObject(filter1059, "since", (double)since_1059);
cJSON_AddNumberToObject(filter1059, "limit", 400);
{
char* filter_str = cJSON_PrintUnformatted(filter1059);
DEBUG_TRACE("[didactyl] DEBUG DM subscription filter kind1059: %s", filter_str ? filter_str : "<null>");
DEBUG_TRACE("[didactyl] DEBUG kind1059 since=%ld start=%ld", (long)since_1059, (long)g_start_time);
free(filter_str);
}
nostr_pool_subscription_t* sub1059 = nostr_relay_pool_subscribe(
g_pool,
(const char**)g_cfg->relays,
g_cfg->relay_count,
filter1059,
on_event,
on_eose,
NULL,
0,
0,
NOSTR_POOL_EOSE_FULL_SET,
30,
120);
cJSON_Delete(filter1059);
if (!sub1059) {
fprintf(stderr, "[didactyl] kind1059 DM subscription failed\n");
return -1;
}
subscribed_any = 1;
DEBUG_TRACE("[didactyl] DEBUG kind1059 DM subscription sub=%p", (void*)sub1059);
}
if (!subscribed_any) {
return -1;
}
DEBUG_INFO("[didactyl] DM subscription active for pubkey %.16s...", g_cfg->keys.public_key_hex);
DEBUG_TRACE("[didactyl] DEBUG DM subscription sub=%p close_on_eose=0 dedup=1", (void*)sub);
DEBUG_TRACE("[didactyl] DEBUG DM subscription g_start_time=%ld now=%ld delta=%ld relay_count=%d",
(long)g_start_time, (long)time(NULL), (long)(time(NULL) - g_start_time), g_cfg->relay_count);
return 0;
}
@@ -1186,21 +1278,6 @@ int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message)
}
}
int* pre_publish_ok = NULL;
if (connected_count > 0) {
pre_publish_ok = (int*)calloc((size_t)connected_count, sizeof(int));
if (!pre_publish_ok) {
free(connected_relays);
cJSON_Delete(event);
return -1;
}
for (int i = 0; i < connected_count; i++) {
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, connected_relays[i]);
pre_publish_ok[i] = stats ? stats->events_published_ok : 0;
}
}
int sent = 0;
if (connected_count > 0) {
sent = nostr_relay_pool_publish_async(
@@ -1214,48 +1291,6 @@ int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message)
for (int i = 0; i < connected_count; i++) {
DEBUG_INFO("[didactyl] kind 4 event published to %s (async)", connected_relays[i]);
}
// Briefly drain relay messages so NIP-42 AUTH handshake can complete.
for (int i = 0; i < 5; i++) {
(void)nostr_relay_pool_poll(g_pool, 100);
}
int any_publish_ok = 0;
int auth_required_seen = 0;
for (int i = 0; i < connected_count; i++) {
const nostr_relay_stats_t* stats = nostr_relay_pool_get_relay_stats(g_pool, connected_relays[i]);
if (stats && stats->events_published_ok > pre_publish_ok[i]) {
any_publish_ok = 1;
}
const char* pub_err = nostr_relay_pool_get_relay_last_publish_error(g_pool, connected_relays[i]);
if (pub_err && strstr(pub_err, "auth-required") != NULL) {
auth_required_seen = 1;
}
}
// Always retry this same signed event once after handshake drain.
// Some relays issue AUTH challenge for the first EVENT and only accept
// the subsequent resend after AUTH succeeds.
if (sent > 0) {
if (auth_required_seen || !any_publish_ok) {
DEBUG_WARN("[didactyl] retrying kind 4 event once after auth handshake window");
}
int resent = nostr_relay_pool_publish_async(
g_pool,
connected_relays,
connected_count,
event,
NULL,
NULL);
if (resent > sent) {
sent = resent;
}
for (int i = 0; i < 5; i++) {
(void)nostr_relay_pool_poll(g_pool, 100);
}
}
} else {
DEBUG_WARN("[didactyl] kind 4 event not queued: no connected relays");
}
@@ -1268,7 +1303,6 @@ int nostr_handler_send_dm(const char* recipient_pubkey_hex, const char* message)
recipient_pubkey_hex,
sent);
free(pre_publish_ok);
free(connected_relays);
cJSON_Delete(event);
return sent > 0 ? 0 : -1;
@@ -1793,79 +1827,22 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me
return -1;
}
cJSON* filter = cJSON_CreateObject();
cJSON* kinds = cJSON_CreateArray();
cJSON* authors = cJSON_CreateArray();
if (!filter || !kinds || !authors) {
cJSON_Delete(filter);
cJSON_Delete(kinds);
cJSON_Delete(authors);
return -1;
}
cJSON_AddItemToArray(kinds, cJSON_CreateNumber(10050));
cJSON_AddItemToObject(filter, "kinds", kinds);
cJSON_AddItemToArray(authors, cJSON_CreateString(recipient_pubkey_hex));
cJSON_AddItemToObject(filter, "authors", authors);
cJSON_AddNumberToObject(filter, "limit", 1);
int event_count = 0;
cJSON** events = nostr_relay_pool_query_sync(
g_pool,
(const char**)g_cfg->relays,
g_cfg->relay_count,
filter,
&event_count,
5000);
cJSON_Delete(filter);
char* extracted_dm_relays[NIP17_MAX_RELAYS] = {0};
int extracted_count = 0;
if (events && event_count > 0 && events[0]) {
int extracted = nostr_nip17_extract_dm_relays(events[0], extracted_dm_relays, NIP17_MAX_RELAYS);
if (extracted > 0) {
extracted_count = extracted;
}
}
if (events) {
for (int i = 0; i < event_count; i++) {
cJSON_Delete(events[i]);
}
free(events);
}
const char* target_relays[NIP17_MAX_RELAYS] = {0};
int target_count = 0;
if (extracted_count > 0) {
for (int i = 0; i < g_cfg->relay_count && target_count < NIP17_MAX_RELAYS; i++) {
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) != NOSTR_POOL_RELAY_CONNECTED) {
continue;
}
for (int j = 0; j < extracted_count; j++) {
if (extracted_dm_relays[j] && strcmp(g_cfg->relays[i], extracted_dm_relays[j]) == 0) {
target_relays[target_count++] = g_cfg->relays[i];
break;
}
}
for (int i = 0; i < g_cfg->relay_count && target_count < NIP17_MAX_RELAYS; i++) {
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) == NOSTR_POOL_RELAY_CONNECTED) {
target_relays[target_count++] = g_cfg->relays[i];
}
}
if (target_count == 0) {
for (int i = 0; i < g_cfg->relay_count && target_count < NIP17_MAX_RELAYS; i++) {
if (nostr_relay_pool_get_relay_status(g_pool, g_cfg->relays[i]) == NOSTR_POOL_RELAY_CONNECTED) {
target_relays[target_count++] = g_cfg->relays[i];
}
}
}
for (int i = 0; i < extracted_count; i++) {
free(extracted_dm_relays[i]);
}
DEBUG_TRACE("[didactyl] NIP-17 send prep: recipient=%.16s... connected_targets=%d",
recipient_pubkey_hex,
target_count);
if (target_count <= 0) {
DEBUG_WARN("[didactyl] NIP-17 send aborted: no connected relays for recipient %.16s...",
recipient_pubkey_hex);
return -1;
}
@@ -1892,14 +1869,19 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me
0);
cJSON_Delete(chat_event);
if (gift_count <= 0) {
DEBUG_WARN("[didactyl] NIP-17 send aborted: gift_wrap creation failed for %.16s...",
recipient_pubkey_hex);
return -1;
}
int any_sent = 0;
int sent_count = 0;
for (int i = 0; i < gift_count; i++) {
if (!gift_wraps[i]) {
continue;
}
int sent = nostr_relay_pool_publish_async(g_pool,
target_relays,
target_count,
@@ -1908,15 +1890,27 @@ int nostr_handler_send_dm_nip17(const char* recipient_pubkey_hex, const char* me
NULL);
if (sent > 0) {
any_sent = 1;
sent_count += sent;
}
for (int r = 0; r < target_count; r++) {
DEBUG_INFO("[didactyl] kind 1059 event published to %s (async)", target_relays[r]);
}
cJSON_Delete(gift_wraps[i]);
}
for (int i = 0; i < 5; i++) {
(void)nostr_relay_pool_poll(g_pool, 100);
DEBUG_TRACE("[didactyl] NIP-17 send complete: recipient=%.16s... sent_count=%d",
recipient_pubkey_hex,
sent_count);
if (!any_sent) {
DEBUG_WARN("[didactyl] NIP-17 send failed: no relay accepted publish for recipient %.16s...",
recipient_pubkey_hex);
return -1;
}
return any_sent ? 0 : -1;
return 0;
}
char* nostr_handler_get_admin_kind0_context(void) {
@@ -1998,9 +1992,38 @@ int nostr_handler_poll(int timeout_ms) {
return -1;
}
double start_ms = -1.0;
struct timespec ts_start;
if (clock_gettime(CLOCK_MONOTONIC, &ts_start) == 0) {
start_ms = ts_start.tv_sec * 1000.0 + ts_start.tv_nsec / 1000000.0;
}
int rc = nostr_relay_pool_poll(g_pool, timeout_ms);
g_poll_counter++;
if (start_ms >= 0.0) {
struct timespec ts_end;
if (clock_gettime(CLOCK_MONOTONIC, &ts_end) == 0) {
const double end_ms = ts_end.tv_sec * 1000.0 + ts_end.tv_nsec / 1000000.0;
const double elapsed_ms = end_ms - start_ms;
const double expected_ms = timeout_ms > 0 ? (double)timeout_ms : 0.0;
if (elapsed_ms > expected_ms + 250.0) {
DEBUG_WARN("[didactyl] poll latency spike: nostr_relay_pool_poll(timeout=%d) took %.1fms rc=%d count=%llu",
timeout_ms,
elapsed_ms,
rc,
(unsigned long long)g_poll_counter);
} else if ((g_poll_counter % 200ULL) == 0ULL) {
DEBUG_TRACE("[didactyl] poll heartbeat: timeout=%d elapsed=%.1fms rc=%d count=%llu",
timeout_ms,
elapsed_ms,
rc,
(unsigned long long)g_poll_counter);
}
}
}
log_relay_state_changes();
return rc;
+4 -4
View File
@@ -268,7 +268,7 @@ int trigger_manager_load_from_skills(trigger_manager_t* mgr) {
cJSON_AddItemToObject(adopt_filter, "authors", authors);
cJSON_AddNumberToObject(adopt_filter, "limit", 1);
char* adoption_json = nostr_handler_query_json(adopt_filter, 8000);
char* adoption_json = nostr_handler_query_json(adopt_filter, 2000);
cJSON_Delete(adopt_filter);
if (!adoption_json) {
return 0;
@@ -328,7 +328,7 @@ int trigger_manager_load_from_skills(trigger_manager_t* mgr) {
cJSON_AddItemToObject(skill_filter, "#d", d_values);
cJSON_AddNumberToObject(skill_filter, "limit", 1);
char* skill_json = nostr_handler_query_json(skill_filter, 8000);
char* skill_json = nostr_handler_query_json(skill_filter, 2000);
cJSON_Delete(skill_filter);
if (!skill_json) {
continue;
@@ -513,7 +513,7 @@ int trigger_manager_poll(trigger_manager_t* mgr) {
}
time_t now = time(NULL);
if (mgr->last_poll_at > 0 && (now - mgr->last_poll_at) < 2) {
if (mgr->last_poll_at > 0 && (now - mgr->last_poll_at) < 10) {
return 0;
}
mgr->last_poll_at = now;
@@ -538,7 +538,7 @@ int trigger_manager_poll(trigger_manager_t* mgr) {
cJSON_AddNumberToObject(filter, "since", (double)since);
cJSON_AddNumberToObject(filter, "limit", 8);
char* events_json = nostr_handler_query_json(filter, 4000);
char* events_json = nostr_handler_query_json(filter, 1200);
cJSON_Delete(filter);
if (!events_json) {
continue;