libnostr-c provides a comprehensive C implementation of the Nostr protocol. This document describes all public APIs available in the library.
nostr_error_t nostr_init(void);Initialize the libnostr-c library. Must be called before using any other functions.
Returns: NOSTR_OK on success, error code otherwise.
void nostr_cleanup(void);Clean up the libnostr-c library. Should be called when done using the library.
typedef enum {
NOSTR_OK = 0,
NOSTR_ERR_INVALID_KEY,
NOSTR_ERR_INVALID_EVENT,
NOSTR_ERR_INVALID_SIGNATURE,
NOSTR_ERR_MEMORY,
NOSTR_ERR_JSON_PARSE,
NOSTR_ERR_ENCODING,
NOSTR_ERR_CONNECTION,
NOSTR_ERR_PROTOCOL,
NOSTR_ERR_NOT_FOUND,
NOSTR_ERR_TIMEOUT,
NOSTR_ERR_INVALID_PARAM
} nostr_error_t;Error codes returned by libnostr-c functions.
const char* nostr_error_string(nostr_error_t error);Get human-readable error message for an error code.
typedef struct nostr_key {
uint8_t data[NOSTR_PUBKEY_SIZE];
} nostr_key;Public key structure (32 bytes).
typedef struct nostr_privkey {
uint8_t data[NOSTR_PRIVKEY_SIZE];
} nostr_privkey;Private key structure (32 bytes).
nostr_error_t nostr_key_generate(nostr_privkey* privkey, nostr_key* pubkey);Generate a new cryptographically secure keypair.
Parameters:
privkey: Output private keypubkey: Output public key
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_key_from_hex(const char* hex, nostr_key* key);Convert hex string to public key.
Parameters:
hex: 64-character hex stringkey: Output public key
Returns: NOSTR_OK on success, NOSTR_ERR_ENCODING on invalid hex.
nostr_error_t nostr_key_to_hex(const nostr_key* key, char* hex, size_t hex_size);Convert public key to hex string.
Parameters:
key: Input public keyhex: Output buffer (minimum 65 bytes)hex_size: Size of output buffer
Returns: NOSTR_OK on success, NOSTR_ERR_INVALID_PARAM if buffer too small.
nostr_error_t nostr_key_to_bech32(const nostr_key* key, const char* prefix,
char* bech32, size_t bech32_size);Convert public key to bech32 format.
Parameters:
key: Input public keyprefix: Bech32 prefix (e.g., "npub")bech32: Output bufferbech32_size: Size of output buffer
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_key_from_bech32(const char* bech32, nostr_key* key);Convert bech32 string to public key.
Parameters:
bech32: Input bech32 stringkey: Output public key
Returns: NOSTR_OK on success, NOSTR_ERR_ENCODING on invalid bech32.
typedef struct nostr_tag {
char** values;
size_t count;
} nostr_tag;Event tag structure containing array of string values.
typedef struct nostr_event {
uint8_t id[NOSTR_ID_SIZE];
nostr_key pubkey;
int64_t created_at;
uint16_t kind;
nostr_tag* tags;
size_t tags_count;
char* content;
uint8_t sig[NOSTR_SIG_SIZE];
} nostr_event;Nostr event structure containing all event data.
nostr_error_t nostr_event_create(nostr_event** event);Create a new event structure.
Parameters:
event: Output event pointer
Returns: NOSTR_OK on success, NOSTR_ERR_MEMORY on allocation failure.
void nostr_event_destroy(nostr_event* event);Destroy an event and free all associated memory.
Parameters:
event: Event to destroy
nostr_error_t nostr_event_set_content(nostr_event* event, const char* content);Set event content string.
Parameters:
event: Event to modifycontent: Content string to set
Returns: NOSTR_OK on success, NOSTR_ERR_MEMORY on allocation failure.
nostr_error_t nostr_event_add_tag(nostr_event* event, const char** values, size_t count);Add a tag to an event.
Parameters:
event: Event to modifyvalues: Array of tag valuescount: Number of values
Returns: NOSTR_OK on success, NOSTR_ERR_MEMORY on allocation failure.
nostr_error_t nostr_event_compute_id(nostr_event* event);Compute event ID (SHA256 hash) according to NIP-01.
Parameters:
event: Event to compute ID for
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_event_sign(nostr_event* event, const nostr_privkey* privkey);Sign an event with a private key.
Parameters:
event: Event to signprivkey: Private key for signing
Returns: NOSTR_OK on success, NOSTR_ERR_INVALID_SIGNATURE on failure.
nostr_error_t nostr_event_verify(const nostr_event* event);Verify event signature.
Parameters:
event: Event to verify
Returns: NOSTR_OK if valid, NOSTR_ERR_INVALID_SIGNATURE if invalid.
nostr_error_t nostr_event_to_json(const nostr_event* event, char** json);Serialize event to JSON string.
Parameters:
event: Event to serializejson: Output JSON string (caller must free)
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_event_from_json(const char* json, nostr_event** event);Parse event from JSON string.
Parameters:
json: Input JSON stringevent: Output event pointer
Returns: NOSTR_OK on success, NOSTR_ERR_JSON_PARSE on invalid JSON.
typedef enum {
NOSTR_RELAY_DISCONNECTED = 0,
NOSTR_RELAY_CONNECTING,
NOSTR_RELAY_CONNECTED,
NOSTR_RELAY_ERROR
} nostr_relay_state;Relay connection states.
typedef struct nostr_relay {
char* url;
nostr_relay_state state;
void* ws_handle;
void* user_data;
nostr_message_callback message_callback;
void* message_user_data;
} nostr_relay;Relay connection structure.
typedef void (*nostr_relay_callback)(struct nostr_relay* relay,
nostr_relay_state state, void* user_data);Callback for relay state changes.
typedef void (*nostr_event_callback)(const nostr_event* event, void* user_data);Callback for received events.
typedef void (*nostr_message_callback)(const char* message_type,
const char* data, void* user_data);Callback for relay protocol messages.
nostr_error_t nostr_relay_create(nostr_relay** relay, const char* url);Create a new relay connection.
Parameters:
relay: Output relay pointerurl: WebSocket URL (e.g., "wss://relay.damus.io")
Returns: NOSTR_OK on success, error code otherwise.
void nostr_relay_destroy(nostr_relay* relay);Destroy a relay connection and free resources.
Parameters:
relay: Relay to destroy
nostr_error_t nostr_relay_connect(nostr_relay* relay, nostr_relay_callback callback,
void* user_data);Connect to a relay.
Parameters:
relay: Relay instancecallback: State change callbackuser_data: User data for callback
Returns: NOSTR_OK on success, NOSTR_ERR_CONNECTION on failure.
nostr_error_t nostr_relay_disconnect(nostr_relay* relay);Disconnect from a relay.
Parameters:
relay: Relay instance
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_publish_event(nostr_relay* relay, const nostr_event* event);Publish an event to a relay.
Parameters:
relay: Relay instanceevent: Event to publish
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_subscribe(nostr_relay* relay, const char* subscription_id,
const char* filters_json, nostr_event_callback callback,
void* user_data);Subscribe to events matching filters.
Parameters:
relay: Relay instancesubscription_id: Unique subscription IDfilters_json: JSON filters (NIP-01 format)callback: Event callbackuser_data: User data for callback
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_relay_unsubscribe(nostr_relay* relay, const char* subscription_id);Unsubscribe from a subscription.
Parameters:
relay: Relay instancesubscription_id: Subscription ID to cancel
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_zap_create_request(nostr_event** event, uint64_t amount,
const nostr_key* recipient, const char* lnurl,
const char* content, const char** relays,
size_t relays_count);Create a zap request event (kind 9734).
Parameters:
event: Output event pointeramount: Amount in millisatsrecipient: Recipient's public keylnurl: Recipient's LNURLcontent: Optional zap messagerelays: Array of relay URLsrelays_count: Number of relays
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_zap_validate_lnurl(const char* lnurl, char* nostr_pubkey,
bool* allows_nostr);Validate an LNURL for zap support.
Parameters:
lnurl: LNURL to validatenostr_pubkey: Output buffer for server's pubkey (optional, 33 bytes)allows_nostr: Output flag for nostr support (optional)
Returns: NOSTR_OK if valid, error code otherwise.
nostr_error_t nostr_zap_parse_receipt(const nostr_event* event, uint64_t* amount,
char** bolt11, char* preimage,
nostr_event** zap_request);Parse zap receipt event (kind 9735).
Parameters:
event: Input zap receipt eventamount: Output amount in millisats (optional)bolt11: Output bolt11 invoice (optional, caller must free)preimage: Output preimage (optional, 64 bytes hex)zap_request: Output zap request event (optional, caller must free)
Returns: NOSTR_OK on success, error code otherwise.
nostr_error_t nostr_zap_verify(const nostr_event* receipt, const nostr_event* request,
const char* server_pubkey);Verify a zap receipt against the original request.
Parameters:
receipt: Zap receipt event (kind 9735)request: Original zap request event (kind 9734)server_pubkey: Expected server's nostr pubkey
Returns: NOSTR_OK if valid, error code otherwise.
libnostr-c provides complete relay-side protocol support for building Nostr relay implementations. Include <nostr_relay_protocol.h> to access these functions.
nostr_relay_error_t nostr_client_msg_parse(const char* json, size_t json_len, nostr_client_msg_t* msg);Parse incoming client messages (EVENT, REQ, CLOSE, AUTH).
void nostr_client_msg_free(nostr_client_msg_t* msg);Free parsed client message resources.
nostr_relay_error_t nostr_filter_parse(const char* json, size_t json_len, nostr_filter_t* filter);Parse a subscription filter from JSON.
bool nostr_filter_matches(const nostr_filter_t* filter, const nostr_event* event);Check if an event matches a filter.
bool nostr_filters_match(const nostr_filter_t* filters, size_t count, const nostr_event* event);Check if an event matches any filter in an array (OR logic).
nostr_relay_error_t nostr_relay_msg_serialize(const nostr_relay_msg_t* msg, char* buf, size_t buf_size, size_t* out_len);Serialize relay messages (EVENT, OK, EOSE, CLOSED, NOTICE, AUTH) to JSON.
void nostr_relay_msg_event(nostr_relay_msg_t* msg, const char* sub_id, const nostr_event* event);
void nostr_relay_msg_ok(nostr_relay_msg_t* msg, const char* event_id, bool success, const char* message);
void nostr_relay_msg_eose(nostr_relay_msg_t* msg, const char* sub_id);
void nostr_relay_msg_closed(nostr_relay_msg_t* msg, const char* sub_id, const char* message);
void nostr_relay_msg_notice(nostr_relay_msg_t* msg, const char* message);
void nostr_relay_msg_auth(nostr_relay_msg_t* msg, const char* challenge);nostr_relay_error_t nostr_event_validate_full(const nostr_event* event, int64_t max_future_seconds, nostr_validation_result_t* result);Full event validation: ID verification, signature check, timestamp bounds, expiration.
nostr_relay_error_t nostr_deletion_parse(const nostr_event* event, nostr_deletion_request_t* request);Parse a kind 5 deletion event.
bool nostr_deletion_authorized(const nostr_deletion_request_t* request, const nostr_event* target_event);Check if deletion is authorized (same pubkey, event ID in list).
bool nostr_deletion_authorized_address(const nostr_deletion_request_t* request, const nostr_event* target_event);Check if deletion is authorized for addressable events (kind 30000-39999).
bool nostr_event_is_expired(const nostr_event* event, int64_t now);Check if event has expired at a given timestamp.
bool nostr_event_is_expired_now(const nostr_event* event);Check if event has expired (uses current time).
nostr_kind_type_t nostr_kind_get_type(int32_t kind);Classify event kind: REGULAR, REPLACEABLE, EPHEMERAL, or ADDRESSABLE.
bool nostr_kind_is_regular(int32_t kind);
bool nostr_kind_is_replaceable(int32_t kind);
bool nostr_kind_is_ephemeral(int32_t kind);
bool nostr_kind_is_addressable(int32_t kind);typedef struct {
const char* name; // relay name
const char* description; // relay description
const char* banner; // banner image URL
const char* icon; // icon image URL
const char* pubkey; // admin pubkey hex
const char* self_pubkey; // relay's own pubkey
const char* contact; // admin contact
int32_t* supported_nips; // array of NIP numbers
size_t supported_nips_count;
const char* software; // software URL
const char* version; // version string
const char* privacy_policy; // privacy policy URL
const char* terms_of_service; // terms of service URL
nostr_relay_limitation_t limitation;
nostr_relay_retention_t* retention;
size_t retention_count;
const char** relay_countries;
size_t relay_countries_count;
const char** language_tags;
size_t language_tags_count;
const char** tags;
size_t tags_count;
const char* posting_policy;
const char* payments_url;
nostr_relay_fees_t fees;
} nostr_relay_info_t;typedef struct {
int32_t max_message_length;
int32_t max_subscriptions;
int32_t max_filters;
int32_t max_limit;
int32_t max_subid_length;
int32_t max_event_tags;
int32_t max_content_length;
int32_t min_pow_difficulty;
bool auth_required;
bool payment_required;
bool restricted_writes;
int64_t created_at_lower_limit;
int64_t created_at_upper_limit;
int32_t default_limit;
} nostr_relay_limitation_t;void nostr_relay_info_init(nostr_relay_info_t* info);Initialize relay info struct with sensible defaults.
void nostr_relay_limitation_init(nostr_relay_limitation_t* limitation);Initialize limitation struct with sensible defaults.
nostr_relay_error_t nostr_relay_info_serialize(const nostr_relay_info_t* info,
char* buf, size_t buf_size, size_t* out_len);Serialize relay info to JSON for NIP-11 HTTP response.
nostr_relay_error_t nostr_relay_info_add_nip(nostr_relay_info_t* info, int32_t nip);Add a single NIP to supported NIPs list.
void nostr_relay_info_free(nostr_relay_info_t* info);Free relay info resources.
typedef struct {
const nostr_event* event;
size_t current_index;
} nostr_tag_iterator_t;void nostr_tag_iterator_init(nostr_tag_iterator_t* iter, const nostr_event* event);Initialize tag iterator for an event.
const char** nostr_tag_iterator_next(nostr_tag_iterator_t* iter, size_t* tag_len);Get next tag as raw array. Returns NULL when no more tags.
bool nostr_tag_iterator_next_info(nostr_tag_iterator_t* iter, nostr_tag_info_t* tag);Get next tag as structured info with name and values separated.
bool nostr_tag_is_indexable(const char* tag_name);Check if tag name is a single letter (indexable per NIP-01).
const char** nostr_filter_get_e_tags(const nostr_filter_t* filter, size_t* count);
const char** nostr_filter_get_p_tags(const nostr_filter_t* filter, size_t* count);
const char** nostr_filter_get_tag_values(const nostr_filter_t* filter, char tag_name, size_t* count);
bool nostr_filter_has_tag_filters(const nostr_filter_t* filter);Access filter tag arrays for query planning and optimization.
#include <nostr_relay_protocol.h>
// Parse incoming client message
nostr_client_msg_t msg;
if (nostr_client_msg_parse(json, len, &msg) == NOSTR_RELAY_OK) {
switch (msg.type) {
case NOSTR_CLIENT_MSG_EVENT:
// Validate and store event
nostr_validation_result_t result;
if (nostr_event_validate_full(msg.data.event.event, 300, &result) == NOSTR_RELAY_OK) {
// Check expiration
if (!nostr_event_is_expired_now(msg.data.event.event)) {
store_event(msg.data.event.event);
}
}
// Send OK response
nostr_relay_msg_t reply;
nostr_relay_msg_ok(&reply, event_id, true, "");
nostr_relay_msg_serialize(&reply, buf, sizeof(buf), NULL);
break;
case NOSTR_CLIENT_MSG_REQ:
// Match events against filters
for (size_t i = 0; i < stored_events_count; i++) {
if (nostr_filters_match(msg.data.req.filters, msg.data.req.filters_count, stored_events[i])) {
send_event(msg.data.req.subscription_id, stored_events[i]);
}
}
// Send EOSE
nostr_relay_msg_eose(&reply, msg.data.req.subscription_id);
break;
}
nostr_client_msg_free(&msg);
}#include <nostr_relay_protocol.h>
// Initialize relay info
nostr_relay_info_t info;
nostr_relay_info_init(&info);
// Set basic info
info.name = "My Relay";
info.description = "A personal nostr relay";
info.pubkey = "abc123..."; // admin pubkey
info.contact = "admin@example.com";
info.software = "https://github.com/user/my-relay";
info.version = "1.0.0";
// Add supported NIPs
nostr_relay_info_add_nip(&info, 1);
nostr_relay_info_add_nip(&info, 9);
nostr_relay_info_add_nip(&info, 11);
// Configure limitations
info.limitation.max_message_length = 128 * 1024;
info.limitation.max_subscriptions = 20;
info.limitation.auth_required = false;
info.limitation.payment_required = false;
// Serialize for HTTP response (Accept: application/nostr+json)
char buf[8192];
size_t len;
if (nostr_relay_info_serialize(&info, buf, sizeof(buf), &len) == NOSTR_RELAY_OK) {
// Send buf as HTTP response with Content-Type: application/nostr+json
}
// Cleanup (only needed if using nostr_relay_info_add_nip)
nostr_relay_info_free(&info);nostr_error_t nostr_event_id_to_bech32(const uint8_t* id, char* bech32, size_t bech32_size);Convert event ID to bech32 format (note).
nostr_error_t nostr_event_id_from_bech32(const char* bech32, uint8_t* id);Convert bech32 string to event ID.
nostr_error_t nostr_privkey_to_bech32(const nostr_privkey* privkey, char* bech32,
size_t bech32_size);Convert private key to bech32 format (nsec).
nostr_error_t nostr_privkey_from_bech32(const char* bech32, nostr_privkey* privkey);Convert bech32 string to private key.
#define NOSTR_PUBKEY_SIZE 32 // Public key size in bytes
#define NOSTR_PRIVKEY_SIZE 32 // Private key size in bytes
#define NOSTR_SIG_SIZE 64 // Signature size in bytes
#define NOSTR_ID_SIZE 32 // Event ID size in bytes// Initialize library
nostr_init();
// Generate keys
nostr_privkey privkey;
nostr_key pubkey;
nostr_key_generate(&privkey, &pubkey);
// Create event
nostr_event* event;
nostr_event_create(&event);
event->kind = 1;
event->pubkey = pubkey;
nostr_event_set_content(event, "Hello, Nostr!");
nostr_event_compute_id(event);
nostr_event_sign(event, &privkey);
// Verify and serialize
nostr_event_verify(event);
char* json;
nostr_event_to_json(event, &json);
// Cleanup
free(json);
nostr_event_destroy(event);
nostr_cleanup();// Create and connect to relay
nostr_relay* relay;
nostr_relay_create(&relay, "wss://relay.damus.io");
nostr_relay_connect(relay, state_callback, NULL);
// Subscribe to events
nostr_subscribe(relay, "sub1", "{\"kinds\":[1]}", event_callback, NULL);
// Publish event
nostr_publish_event(relay, event);
// Cleanup
nostr_relay_disconnect(relay);
nostr_relay_destroy(relay);Always check return values and handle errors appropriately:
nostr_error_t err = nostr_event_create(&event);
if (err != NOSTR_OK) {
fprintf(stderr, "Failed to create event: %s\n", nostr_error_string(err));
return -1;
}libnostr-c is not thread-safe. If using in a multi-threaded environment:
- Use separate library instances per thread, or
- Implement external synchronization around library calls
- Always call
nostr_init()before using the library - Always call
nostr_cleanup()when done - Free all allocated strings returned by the library
- Use
nostr_event_destroy()for events - Use
nostr_relay_destroy()for relays
For more examples and integration patterns, see the examples directory and integration guide.