Files
nostr_core_lib/README.md
2026-07-20 18:34:00 -04:00

31 KiB
Raw Permalink Blame History

NOSTR Core Library

A C library for NOSTR protocol implementation. Work in progress.

Version License Build Status

📋 NIP Implementation Status

Core Protocol NIPs

  • NIP-01 - Basic protocol flow - event creation, signing, and validation
  • NIP-02 - Contact List and Petnames
  • NIP-03 - OpenTimestamps Attestations for Events
  • NIP-04 - Encrypted Direct Messages (legacy)
  • NIP-05 - Mapping Nostr keys to DNS-based internet identifiers
  • NIP-06 - Basic key derivation from mnemonic seed phrase
  • [-] NIP-07 - window.nostr capability for web browsers
  • NIP-08 - Handling Mentions
  • NIP-09 - Event Deletion
  • NIP-10 - Conventions for clients' use of e and p tags in text events
  • NIP-11 - Relay Information Document
  • NIP-12 - Generic Tag Queries
  • NIP-13 - Proof of Work
  • NIP-14 - Subject tag in text events
  • NIP-15 - Nostr Marketplace (for resilient marketplaces)
  • NIP-16 - Event Treatment
  • NIP-17 - Private Direct Messages
  • NIP-18 - Reposts
  • NIP-19 - bech32-encoded entities
  • NIP-20 - Command Results
  • NIP-21 - nostr: URI scheme
  • NIP-22 - Event created_at Limits
  • NIP-23 - Long-form Content
  • NIP-24 - Extra metadata fields and tags
  • NIP-25 - Reactions
  • NIP-26 - Delegated Event Signing
  • NIP-27 - Text Note References
  • NIP-28 - Public Chat
  • NIP-29 - Relay-based Groups
  • NIP-30 - Custom Emoji
  • NIP-31 - Dealing with Unknown Events
  • NIP-32 - Labeling
  • NIP-33 - Parameterized Replaceable Events
  • NIP-34 - git stuff
  • NIP-35 - Torrents
  • NIP-36 - Sensitive Content / Content Warning
  • NIP-37 - Draft Events
  • NIP-38 - User Statuses
  • NIP-39 - External Identities in Profiles
  • NIP-40 - Expiration Timestamp
  • NIP-42 - Authentication of clients to relays
  • NIP-44 - Versioned Encryption
  • NIP-45 - Counting results
  • NIP-46 - Nostr Remote Signing
  • NIP-47 - Wallet Connect
  • NIP-48 - Proxy Tags
  • NIP-49 - Private Key Encryption
  • NIP-50 - Search Capability
  • NIP-51 - Lists
  • NIP-52 - Calendar Events
  • NIP-53 - Live Activities
  • NIP-54 - Wiki
  • [-] NIP-55 - Android Signer Application
  • NIP-56 - Reporting
  • NIP-57 - Lightning Zaps
  • NIP-58 - Badges
  • NIP-59 - Gift Wrap
  • NIP-60 - Cashu Wallet
  • NIP-61 - Nutzaps
  • NIP-62 - Log events
  • [-] NIP-64 - Chess (PGN)
  • NIP-65 - Relay List Metadata
  • NIP-66 - Relay Monitor
  • [-] NIP-68 - Web badges
  • NIP-69 - Peer-to-peer Order events
  • NIP-70 - Protected Events
  • NIP-71 - Video Events
  • NIP-72 - Moderated Communities
  • NIP-73 - External Content IDs
  • NIP-75 - Zap Goals
  • NIP-77 - Arbitrary custom app data
  • NIP-78 - Application-specific data
  • NIP-84 - Highlights
  • NIP-86 - Relay Management API
  • NIP-87 - Relay List Recommendations
  • [-] NIP-88 - Stella: A Stellar Relay
  • NIP-89 - Recommended Application Handlers
  • NIP-90 - Data Vending Machines
  • NIP-92 - Media Attachments
  • NIP-94 - File Metadata
  • NIP-96 - HTTP File Storage Integration
  • NIP-98 - HTTP Auth
  • NIP-99 - Classified Listings

Legend: Fully Implemented | ⚠️ Partial Implementation | Not Implemented | Not Applicable

Implementation Summary: 13 of 96+ NIPs fully implemented (13.5%)

📦 Quick Start

Installation

  1. Clone the repository:

    git clone https://github.com/yourusername/nostr_core_lib.git
    cd nostr_core_lib
    
  2. Build the library:

    ./build.sh
    
  3. Build and run examples:

    ./build.sh -e
    ./examples/simple_keygen
    

Usage Example

#include "nostr_core/nostr_core.h"
#include <stdio.h>

int main() {
    // Initialize library
    if (nostr_init() != NOSTR_SUCCESS) {
        fprintf(stderr, "Failed to initialize NOSTR library\n");
        return 1;
    }
    
    // Generate keypair
    unsigned char private_key[32], public_key[32];
    nostr_generate_keypair(private_key, public_key);
    
    // Convert to bech32 format
    char nsec[100], npub[100];
    nostr_key_to_bech32(private_key, "nsec", nsec);
    nostr_key_to_bech32(public_key, "npub", npub);
    
    printf("Private key: %s\n", nsec);
    printf("Public key:  %s\n", npub);
    
    // Create and sign event
    cJSON* event = nostr_create_and_sign_event(1, "Hello NOSTR!", NULL, private_key, 0);
    if (event) {
        char* json = cJSON_Print(event);
        printf("Event: %s\n", json);
        free(json);
        cJSON_Delete(event);
    }
    
    nostr_cleanup();
    return 0;
}

Compile and run:

gcc example.c -o example ./libnostr_core_x64.a -lz -ldl -lpthread -lm -lssl -lcrypto -lcurl -lsecp256k1
./example

🏗️ Building

Build Targets

./build.sh                    # Build static library (default)
./build.sh --nips=all         # Force build with all available NIPs
./build.sh -t                 # Build all test executables in tests/
./build.sh -e                 # Build all examples in examples/
./build.sh -t -e              # Build library + tests + examples
./build.sh --nips=46          # Build specifically with NIP-046 support
./build.sh --help             # Show all options

Dependencies

Required System Dependencies:

  • GCC or compatible C compiler
  • Standard C library and math library (-lm)
  • OpenSSL development libraries (-lssl -lcrypto)
  • curl development libraries (-lcurl)
  • secp256k1 development libraries (-lsecp256k1)

Install on Ubuntu/Debian:

sudo apt install build-essential libssl-dev libcurl4-openssl-dev libsecp256k1-dev

Install on CentOS/RHEL:

sudo yum install gcc openssl-devel libcurl-devel libsecp256k1-devel

Install on macOS:

brew install openssl curl secp256k1

Still Bundled:

  • cJSON (JSON parsing - internal copy)
  • TinyAES-c (AES encryption for NIP-04)
  • ChaCha20 (stream cipher for NIP-44)

📚 API Documentation

Initialization

int nostr_init(void);                    // Initialize library (call first)
void nostr_cleanup(void);                // Cleanup resources (call last)
const char* nostr_strerror(int error);   // Get error message

Key Management

// Generate random keypair
int nostr_generate_keypair(unsigned char* private_key, unsigned char* public_key);

// Generate from mnemonic
int nostr_generate_mnemonic_and_keys(char* mnemonic, size_t mnemonic_size, 
                                     int account, unsigned char* private_key, 
                                     unsigned char* public_key);

// Derive from existing mnemonic
int nostr_derive_keys_from_mnemonic(const char* mnemonic, int account,
                                    unsigned char* private_key, unsigned char* public_key);

// Format conversion
int nostr_key_to_bech32(const unsigned char* key, const char* hrp, char* output);
nostr_input_type_t nostr_detect_input_type(const char* input);

Event Creation

// Create and sign event
cJSON* nostr_create_and_sign_event(int kind, const char* content, cJSON* tags, 
                                   const unsigned char* private_key, time_t timestamp);

// Add proof of work
int nostr_add_proof_of_work(cJSON* event, const unsigned char* private_key, 
                           int target_difficulty, void (*progress_callback)(...), void* user_data);

Encryption (NIP-04 & NIP-44)

// NIP-04 (AES-CBC)
int nostr_nip04_encrypt(const unsigned char* sender_private_key,
                       const unsigned char* recipient_public_key, 
                       const char* plaintext, char* output, size_t output_size);

int nostr_nip04_decrypt(const unsigned char* recipient_private_key,
                       const unsigned char* sender_public_key,
                       const char* encrypted_data, char* output, size_t output_size);

// NIP-44 (ChaCha20)  
int nostr_nip44_encrypt(const unsigned char* sender_private_key,
                       const unsigned char* recipient_public_key,
                       const char* plaintext, char* output, size_t output_size);

int nostr_nip44_decrypt(const unsigned char* recipient_private_key,
                       const unsigned char* sender_public_key,
                       const char* encrypted_data, char* output, size_t output_size);

Relay Communication

// Simple relay query
cJSON* nostr_query_relay_for_event(const char* relay_url, const char* pubkey_hex, int kind);

// Multi-relay synchronous queries
cJSON** synchronous_query_relays_with_progress(const char** relay_urls, int relay_count,
                                               cJSON* filter, relay_query_mode_t mode,
                                               int* result_count, int relay_timeout_seconds,
                                               relay_progress_callback_t callback, void* user_data);

// Multi-relay publishing
publish_result_t* synchronous_publish_event_with_progress(const char** relay_urls, int relay_count,
                                                         cJSON* event, int* success_count,
                                                         int relay_timeout_seconds,
                                                         publish_progress_callback_t callback, void* user_data);

Relay Pools (Asynchronous)

// Create and manage relay pool with reconnection
nostr_pool_reconnect_config_t* config = nostr_pool_reconnect_config_default();
nostr_relay_pool_t* nostr_relay_pool_create(nostr_pool_reconnect_config_t* config);
int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url);
int nostr_relay_pool_set_auth(nostr_relay_pool_t* pool, const unsigned char* private_key, int enable);
void nostr_relay_pool_destroy(nostr_relay_pool_t* pool);

// Subscribe to events (with auto-reconnection)
nostr_pool_subscription_t* nostr_relay_pool_subscribe(
    nostr_relay_pool_t* pool, const char** relay_urls, int relay_count, cJSON* filter,
    void (*on_event)(cJSON* event, const char* relay_url, void* user_data),
    void (*on_eose)(void* user_data), void* user_data, int close_on_eose);

// Enable NIP-42 auth (auto-responds to relay AUTH challenges)
unsigned char private_key[32];
nostr_hex_to_bytes("91ba716fa9e7ea2fcbad360cf4f8e0d312f73984da63d90f524ad61a6a1e7dbe", private_key, 32);
nostr_relay_pool_set_auth(pool, private_key, 1);

// Run event loop (handles reconnection + incoming AUTH/NOTICE/OK messages)
int nostr_relay_pool_run(nostr_relay_pool_t* pool, int timeout_ms);
int nostr_relay_pool_poll(nostr_relay_pool_t* pool, int timeout_ms);

// Reconnection configuration
typedef struct {
    int enable_auto_reconnect;        // Enable automatic reconnection
    int max_reconnect_attempts;       // Maximum retry attempts
    int initial_reconnect_delay_ms;   // Initial delay between attempts
    int max_reconnect_delay_ms;       // Maximum delay cap
    int reconnect_backoff_multiplier; // Exponential backoff factor
    int ping_interval_seconds;        // Health check ping interval
    int pong_timeout_seconds;         // Pong response timeout
} nostr_pool_reconnect_config_t;

NIP-05 Identifier Verification

// Lookup public key from NIP-05 identifier
int nostr_nip05_lookup(const char* nip05_identifier, char* pubkey_hex_out,
                       char*** relays, int* relay_count, int timeout_seconds);

// Verify NIP-05 identifier against public key
int nostr_nip05_verify(const char* nip05_identifier, const char* pubkey_hex, 
                       char*** relays, int* relay_count, int timeout_seconds);

// Parse .well-known/nostr.json response
int nostr_nip05_parse_well_known(const char* json_response, const char* local_part,
                                 char* pubkey_hex_out, char*** relays, int* relay_count);

NIP-11 Relay Information

// Fetch relay information document
int nostr_nip11_fetch_relay_info(const char* relay_url, nostr_relay_info_t** info_out, int timeout_seconds);

// Free relay information structure
void nostr_nip11_relay_info_free(nostr_relay_info_t* info);

Signer abstraction & nsigner integration

All signing/encryption operations can run through an opaque nostr_signer_t handle. This provides:

  • LOCAL backend (nostr_signer_local) using a raw 32-byte private key (output-equivalent to legacy raw-key APIs).
  • REMOTE backends (nostr_signer_nsigner_*) that call a running n_signer process/device over supported transports.

Core signer verbs (exact signatures)

/* Lifecycle */
nostr_signer_t* nostr_signer_local(const unsigned char private_key[32]);
void nostr_signer_free(nostr_signer_t* signer);

/* Core verbs */
int nostr_signer_get_public_key(nostr_signer_t* signer, char out_pubkey_hex[65]);
int nostr_signer_sign_event(nostr_signer_t* signer, const cJSON* unsigned_event, cJSON** signed_event_out);

/* Encryption verbs */
int nostr_signer_nip04_encrypt(nostr_signer_t* signer,
                               const char* peer_pubkey_hex,
                               const char* plaintext,
                               char** ciphertext_out);
int nostr_signer_nip04_decrypt(nostr_signer_t* signer,
                               const char* peer_pubkey_hex,
                               const char* ciphertext,
                               char** plaintext_out);
int nostr_signer_nip44_encrypt(nostr_signer_t* signer,
                               const char* peer_pubkey_hex,
                               const char* plaintext,
                               char** ciphertext_out);
int nostr_signer_nip44_decrypt(nostr_signer_t* signer,
                               const char* peer_pubkey_hex,
                               const char* ciphertext,
                               char** plaintext_out);

Backends / transports

Remote signer factories (available when NOSTR_ENABLE_NSIGNER_CLIENT is enabled):

nostr_signer_t* nostr_signer_nsigner_unix(const char* socket_name, const char* role, int timeout_ms);
nostr_signer_t* nostr_signer_nsigner_serial(const char* device_path, const char* role, int timeout_ms);
nostr_signer_t* nostr_signer_nsigner_tcp(const char* host, int port, const char* role, int timeout_ms);
nostr_signer_t* nostr_signer_nsigner_fds(int read_fd, int write_fd, const char* role, int timeout_ms);
int nostr_signer_nsigner_set_auth(nostr_signer_t* signer,
                                  const unsigned char auth_privkey[32],
                                  const char* label);

Transport layer constructors and discovery helpers:

nsigner_transport_t* nsigner_transport_open_unix(const char* socket_name, int timeout_ms);
nsigner_transport_t* nsigner_transport_open_tcp(const char* host, int port, int timeout_ms);
nsigner_transport_t* nsigner_transport_open_serial(const char* device_path, int timeout_ms);
nsigner_transport_t* nsigner_transport_open_fds(int read_fd, int write_fd, int timeout_ms);
int nsigner_transport_list_unix(char names[][64], int max_names);
int nsigner_transport_list_serial(char paths[][64], int max_paths);

Supported transport modes:

  • UNIX abstract socket (unix:<name>, no leading @ in API input)
  • USB CDC-ACM serial (serial:/dev/ttyACM0)
  • TCP (tcp:<host>:<port>)
  • Existing framed fd pair (fds:<read_fd>:<write_fd>, useful for stdio/qrexec-style bridges)

Build gating:

  • Desktop builds enable NOSTR_ENABLE_NSIGNER_CLIENT.
  • ESP32 builds exclude the nsigner client transport/client sources and do not define the flag.
  • n_signers own build keeps this client side disabled.

Wire contract (high level):

  • Framing is 4-byte big-endian length + JSON payload.
  • Payload length must be in [1, 65536] bytes.
  • TCP uses a kind-27235 auth envelope (nsigner_rpc, nsigner_method, nsigner_body_hash).
  • See n_signer docs for authoritative protocol details.

Using a signer in higher-level APIs

Pattern: existing raw-private-key functions remain unchanged; signer-aware siblings are provided as *_with_signer variants.

Covered modules and functions:

  • NIP-01 (nostr_core/nip001.h)

    • cJSON* nostr_create_and_sign_event_with_signer(int kind, const char* content, cJSON* tags, nostr_signer_t* signer, time_t timestamp);
  • NIP-17 (nostr_core/nip017.h)

    • cJSON* nostr_nip17_create_relay_list_event_with_signer(const char** relay_urls, int num_relays, nostr_signer_t* signer);
    • int nostr_nip17_send_dm_with_signer(cJSON* dm_event, const char** recipient_pubkeys, int num_recipients, nostr_signer_t* signer, cJSON** gift_wraps_out, int max_gift_wraps, long max_delay_sec);
    • cJSON* nostr_nip17_receive_dm_with_signer(cJSON* gift_wrap, nostr_signer_t* signer);
  • NIP-42 (nostr_core/nip042.h)

    • cJSON* nostr_nip42_create_auth_event_with_signer(const char* challenge, const char* relay_url, nostr_signer_t* signer, time_t timestamp);
  • NIP-46 client/event helpers (nostr_core/nip046.h)

    • cJSON* nostr_nip46_create_request_event_with_signer(const nostr_nip46_request_t* request, nostr_signer_t* signer, const unsigned char* recipient_public_key, time_t timestamp);
    • cJSON* nostr_nip46_create_response_event_with_signer(const nostr_nip46_response_t* response, nostr_signer_t* signer, const unsigned char* recipient_public_key, time_t timestamp);
    • int nostr_nip46_decrypt_event_with_signer(cJSON* event, nostr_signer_t* signer, char** output_out);
    • int nostr_nip46_client_session_init_with_signer(nostr_nip46_client_session_t* session, nostr_signer_t* signer, const char* bunker_url);
  • NIP-60 (nostr_core/nip060.h)

    • cJSON* nostr_nip60_create_wallet_event_with_signer(const nostr_nip60_wallet_data_t* wallet_data, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip60_create_token_event_with_signer(const nostr_nip60_token_data_t* token_data, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip60_create_token_deletion_with_signer(const char* token_event_id, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip60_create_rollover_token_with_signer(const nostr_nip60_token_data_t* remaining_proofs, const char** deleted_event_ids, int deleted_count, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip60_create_history_event_with_signer(const nostr_nip60_history_data_t* history_data, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip60_create_quote_event_with_signer(const char* quote_id, const char* mint_url, time_t expiration, nostr_signer_t* signer, time_t timestamp);
  • NIP-61 (nostr_core/nip061.h)

    • cJSON* nostr_nip61_create_nutzap_info_event_with_signer(const nostr_nip61_nutzap_info_t* info, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip61_create_nutzap_event_with_signer(const nostr_nip61_nutzap_data_t* nutzap_data, nostr_signer_t* signer, time_t timestamp);
    • cJSON* nostr_nip61_create_redemption_event_with_signer(const char* nutzap_event_id, const char* nutzap_relay_hint, const char* sender_pubkey, const char* created_token_event_id, const char* created_token_relay_hint, uint64_t amount, nostr_signer_t* signer, time_t timestamp);
  • Blossom auth/upload/delete (nostr_core/blossom_client.h)

    • char* blossom_create_auth_header_with_signer(nostr_signer_t* signer, const char* operation, const char* sha256_hex, int expiration_seconds, time_t timestamp);
    • int blossom_upload_with_signer(const char* server_url, const unsigned char* data, size_t data_len, const char* content_type, nostr_signer_t* signer, const char* sha256_hex, int timeout_seconds, blossom_blob_descriptor_t* descriptor_out);
    • int blossom_upload_file_with_signer(const char* server_url, const char* file_path, const char* content_type, nostr_signer_t* signer, int timeout_seconds, blossom_blob_descriptor_t* descriptor_out);
    • int blossom_delete_with_signer(const char* server_url, const char* sha256_hex, nostr_signer_t* signer, int timeout_seconds);
  • Relay-pool NIP-42 AUTH entry point (nostr_core/nostr_core.h)

    • int nostr_relay_pool_set_auth_with_signer(nostr_relay_pool_t* pool, nostr_signer_t* signer, int enable);

Driver app: examples/note_poster

Signer modes:

./examples/note_poster

Default is local signer mode; this prompts for nsec1... or 64-char hex private key.

WARNING: test-key-only flow. Do not use a production key; input key material is held in process memory in plaintext.

./examples/note_poster --signer unix:<name>
./examples/note_poster --signer serial:/dev/ttyACM0
./examples/note_poster --signer tcp:<host>:<port>
./examples/note_poster --signer fds:<read_fd>:<write_fd>

Boundaries / what stays in n_signer

nostr_core_lib provides the client side only (transports + nsigner client + signer backends). The following stay in the n_signer project:

  • process spawning/embedding and deployment packaging
  • signer program trust-boundary features (mnemonic handling, approvals, role policy)
  • hardware firmware and platform/device-specific runtime pieces

Dependency direction remains one-way: n_signer -> nostr_core_lib.

Operations requiring raw secret material outside signer verbs are not remotable as-is. Example: mint-protocol operations in Cashu mint pieces (cashu_mint) that require direct secret access beyond event signing/NIP-04/NIP-44.

For implementation plan context see plans/nsigner_integration_plan.md. This integration supersedes per-project hand-rolled signer clients (future migration milestone M7).

📁 Examples

The library includes comprehensive examples:

  • simple_keygen - Basic key generation and formatting
  • keypair_generation - Advanced key management
  • mnemonic_generation - BIP39 mnemonic handling
  • mnemonic_derivation - NIP-06 key derivation
  • utility_functions - General utility demonstrations
  • input_detection - Input type detection and processing
  • relay_pool - Interactive relay pool and subscription testing
  • send_nip17_dm - NIP-17 private direct message send flow
  • nip46_remote_signer - NIP-46 remote signer flow and bunker URL generation
  • version_test - Library version information

Build all examples:

./build.sh -e
ls -la examples/

🧪 Testing

The library includes extensive tests:

# Build all test executables
./build.sh -t

# Run the new NIP-46 test
./tests/nip46_test

# Run selected tests
./tests/nip44_test
./tests/nip42_test
./tests/nip17_test

Current test binaries live in tests/ and are generated from *_test.c sources.

🏗️ Integration

  1. Install system dependencies first (see Dependencies section above)

  2. Copy required files to your project:

    cp libnostr_core_x64.a /path/to/your/project/
    cp nostr_core/nostr_core.h /path/to/your/project/
    
  3. Link in your project:

    gcc your_code.c -L. -lnostr_core -lssl -lcrypto -lcurl -lsecp256k1 -lm -o your_program
    

Source Integration

  1. Install system dependencies first (see Dependencies section above)

  2. Copy source files:

    cp -r nostr_core/ /path/to/your/project/
    cp -r cjson/ /path/to/your/project/
    
  3. Include in your build:

    gcc your_code.c nostr_core/*.c cjson/cJSON.c -lssl -lcrypto -lcurl -lsecp256k1 -lm -o your_program
    

System Dependencies Library

The libnostr_core.a library now uses system dependencies for all major crypto libraries:

  • Uses system OpenSSL (-lssl -lcrypto)
  • Uses system curl (-lcurl)
  • Uses system secp256k1 (-lsecp256k1)
  • Includes only internal code (cJSON, TinyAES, ChaCha20)

Complete linking example:

gcc your_app.c ./libnostr_core_x64.a -lz -ldl -lpthread -lm -lssl -lcrypto -lcurl -lsecp256k1 -o your_app

Check system dependencies:

ldd your_app  # Shows linked system libraries

🔧 Configuration

Compile-Time Options

// Enable debug output
#define NOSTR_DEBUG_ENABLED

// Crypto-only build (no networking)
#define NOSTR_CRYPTO_ONLY

// Enable specific NIPs
#define NOSTR_NIP04_ENABLED
#define NOSTR_NIP44_ENABLED
#define NOSTR_NIP13_ENABLED

Build Flags

# Enable websocket and PoW debug emission (now callback-based)
make LOGGING_FLAGS="-DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING"

# Debug build
make debug

# ARM64 cross-compile
make arm64

Logging Integration (Consumer-Controlled)

nostr_core_lib now supports a callback logging API so host applications can route library logs into their own logger and destination.

#include "nostr_core/nostr_core.h"

static void my_log_cb(int level, const char* component, const char* message, void* user_data) {
    (void)user_data;
    fprintf(stderr, "[nostr][%s][%d] %s\n", component ? component : "unknown", level, message ? message : "");
}

int main(void) {
    if (nostr_init() != NOSTR_SUCCESS) return 1;

    nostr_set_log_callback(my_log_cb, NULL);
    nostr_set_log_level(NOSTR_LOG_LEVEL_TRACE);

    /* ... your code ... */

    nostr_cleanup();
    return 0;
}

Migration Notes

🌐 Supported Platforms

  • Linux (x86_64, ARM64)
  • macOS (Intel, Apple Silicon)
  • Windows (MinGW, MSYS2)
  • Embedded Systems (resource-constrained environments)

🔌 Embedded (ESP32) Support

nostr_core_lib now includes a platform abstraction layer and ESP32-native implementations for core networking and entropy APIs, while preserving desktop compatibility.

Implemented embedded capabilities include:

  • platform random source abstraction via nostr_platform_random()
  • ESP32 random provider using esp_fill_random
  • ESP32 HTTP client implementation via esp_http_client
  • ESP32 WebSocket/WSS transport via esp_transport_ws + TLS certificate bundle
  • NIP-04 encrypted DM flow validated on-device against public relays

Reference ESP-IDF example project (in this workspace):

  • ../esp32_send_kind4/ — Wi-Fi connect + wss:// relay connect + send two kind-4 encrypted events + print relay responses

📄 Documentation

🤝 Contributing

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes and add tests
  4. Build tests and run key suites: ./build.sh -t && ./tests/nip46_test
  5. Commit your changes: git commit -m 'Add amazing feature'
  6. Push to the branch: git push origin feature/amazing-feature
  7. Open a Pull Request

📈 Version History

Current version: 0.6.0

The library uses automatic semantic versioning based on Git tags. Each build increments the patch version automatically.

Recent Developments:

  • ESP32 Embedded Enablement: Added PAL-based embedded support directly in nostr_core_lib
  • ESP32 HTTP + WSS: Added ESP-IDF-backed HTTP and secure websocket transport implementations
  • NIP-04 On-Device Validation: Verified encrypted kind-4 DM publish flow from ESP32 to live relays
  • NIP-05 Support: DNS-based internet identifier verification
  • NIP-11 Support: Relay information document fetching and parsing
  • NIP-19 Support: Bech32-encoded entities (nsec/npub)
  • Comprehensive Testing: Extensive test suite and error handling

Version Timeline:

  • v0.6.x - Embedded-capable releases with ESP32 PAL, HTTP, and WSS support
  • v0.4.x - Development releases with relay pool and expanded NIP support
  • v0.2.x - Earlier OpenSSL-based development releases
  • v0.1.x - Initial development releases
  • Focus on core protocol implementation and OpenSSL-based crypto
  • Full NIP-01, NIP-04, NIP-05, NIP-06, NIP-11, NIP-13, NIP-17, NIP-19, NIP-21, NIP-42, NIP-44, NIP-46, NIP-59 support

🐛 Troubleshooting

Common Issues

Build fails with secp256k1 errors:

# Install secp256k1 with Schnorr support
sudo apt install libsecp256k1-dev    # Ubuntu/Debian
# or
sudo yum install libsecp256k1-devel  # CentOS/RHEL
# or
brew install secp256k1               # macOS

# If still failing, build from source with Schnorr support:
git clone https://github.com/bitcoin-core/secp256k1.git
cd secp256k1
./autogen.sh
./configure --enable-module-schnorrsig --enable-module-ecdh
make
sudo make install

Library size: The library is small (~500KB) as it links against system libraries (secp256k1, OpenSSL, curl) rather than including them statically. This keeps the binary size manageable while maintaining full functionality.

Linking errors: Make sure to include the math library:

gcc your_code.c ./libnostr_core_x64.a -lz -ldl -lpthread -lm -lssl -lcrypto -lcurl -lsecp256k1

Getting Help

  • Check the examples/ directory for working code
  • Run ./build.sh -t to verify your environment
  • Review the comprehensive API documentation in nostr_core/nostr_core.h

📜 License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

  • NOSTR Protocol - The decentralized social media protocol
  • OpenSSL - Production-grade cryptographic library and TLS implementation
  • secp256k1 - Bitcoin's elliptic curve library
  • cJSON - Lightweight JSON parser
  • curl - HTTP/HTTPS client library for NIP-05/NIP-11
  • NOSTR Community - For protocol specification and feedback

Built with ❤️ for the decentralized web

OpenSSL-based • Minimal dependencies • Work in progress