Files
c-relay-pg/plans/status_publish_implementation_plan.md
T

5.1 KiB

Implementation Plan: Republish Kind 1 Status Events to External Relays

Overview

The main relay (c_relay_pg) already does all the work via generate_and_post_status_event():

  • Generates stats text
  • Gets the relay private key
  • Creates and signs a kind 1 event
  • Stores it in the events table
  • Broadcasts to local WebSocket clients

The caching service just needs to detect new kind 1 events by the relay's pubkey and republish them to upstream relays. No key management, no stats generation, no event signing — just a simple poll-and-republish.

Architecture

flowchart TB
    subgraph MainRelay[c_relay_pg - already done]
        Gen[generate_and_post_status_event]
        Store[Store kind 1 event in events table]
        LocalBC[Broadcast to local clients]
        Gen --> Store --> LocalBC
    end

    subgraph CachingService[caching_relay - new]
        Poll[Poll events table for new kind 1\nby relay pubkey]
        Republish[Republish to upstream relays\nvia nostr_relay_pool_publish_async]
        Poll --> Republish
    end

    subgraph PostgreSQL
        Events[events table]
        Config[config table\nrelay_pubkey]
    end

    subgraph UpstreamRelays
        R1[Relay 1]
        R2[Relay 2]
        R3[Relay N]
    end

    Store --> Events
    Config --> Poll
    Events --> Poll
    Republish --> R1
    Republish --> R2
    Republish --> R3

Implementation Steps

Step 1: Add pg_inbox_get_latest_status_event() to pg_inbox

Files: caching/src/pg_inbox.h, caching/src/pg_inbox.c

A function that queries the events table for the most recent kind 1 event by the relay's pubkey. The relay pubkey is read from the config table (same as the main relay does at src/api.c:899).

/* Get the latest kind 1 status event by the relay pubkey.
 * Reads relay_pubkey from config, then queries events table.
 * On success: fills out_event_json with a malloc'd string (caller frees)
 * and out_event_id with the 64-char hex id. Returns 0 on success,
 * -1 on error, 1 if no status event found. */
int pg_inbox_get_latest_status_event(char **out_event_json,
                                      char *out_event_id, int id_len);

SQL:

SELECT e.event_json, e.id
FROM events e
WHERE e.pubkey = $1 AND e.kind = 1
ORDER BY e.created_at DESC
LIMIT 1

Step 2: Add status republish tick to main loop

File: caching/src/main.c

Add a periodic task in the main loop that:

  1. Checks if kind_1_status_posts_hours config is enabled (via pg_inbox_get_config_value())
  2. Polls for the latest kind 1 event by the relay pubkey
  3. If the event ID is different from the last one we republished:
    • Parses the event JSON with cJSON_Parse()
    • Gets the list of connected upstream relay URLs
    • Calls nostr_relay_pool_publish_async(upstream, urls, count, event, callback, NULL)
    • Records the event ID as "last republished"
  4. Runs on a reasonable poll interval (e.g., every 60 seconds) — the actual publish frequency is controlled by the main relay's kind_1_status_posts_hours config, which determines how often new kind 1 events appear in the database

A time_t last_status_check = 0 and char last_republished_id[65] = {0} track state.

The publish callback is a simple logging function (same pattern as sink_publish_cb()).

Event ownership: nostr_relay_pool_publish_async() takes a cJSON* event. We need to verify whether the pool takes ownership or we need to keep it alive. The safest approach: pass the parsed event and free it in the callback, or make a copy. We'll check the nostr_core_lib implementation during coding.

Step 3: Build the caching service

cd caching && make

Step 4: Build and start the test relay on port 8888

./make_and_restart_relay.sh

Production on port 7777 (database crelay_prod) is untouched.

Step 5: Enable status publishing in the test database

PGPASSWORD=crelay psql -h localhost -U crelay -d crelay -c \
  "INSERT INTO config (key, value, type, description, category, editable) \
   VALUES ('kind_1_status_posts_hours', '1', 'int', \
   'Hours between kind 1 status posts', 'relay', 1) \
   ON CONFLICT (key) DO UPDATE SET value='1';"

Step 6: Run the caching service and verify

./build/caching_relay -p "host=127.0.0.1 dbname=crelay user=crelay password=crelay" -d 4

Check caching_relay.log for republish log messages.

Step 7: Verify published events

# Get relay pubkey
PGPASSWORD=crelay psql -h localhost -U crelay -d crelay -c \
  "SELECT value FROM config WHERE key='relay_pubkey';"

# Query an upstream relay
nak req -k 1 -a <relay_pubkey> wss://relay.damus.io

Files to Modify

File Change
caching/src/pg_inbox.h Add pg_inbox_get_latest_status_event() declaration
caching/src/pg_inbox.c Implement pg_inbox_get_latest_status_event()
caching/src/main.c Add status republish tick + publish callback