Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

92 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

libnostr-c

License: MIT C99

A lightweight, portable C library for the Nostr protocol with native Lightning Network integration

What is libnostr-c?

A high-performance C implementation of the Nostr protocol built for efficiency and minimal dependencies. libnostr-c provides core event management, modern NIP-44 encryption powered by noscrypt, Lightning zaps, WebSocket relay communication, and complete relay-side protocol support. Designed for both client applications and relay implementations, with a focus on security, portability, and embedded systems.

API Example

// Client-side
nostr_init();
nostr_keypair_generate();
nostr_event_create();
nostr_event_sign();
nostr_nip44_encrypt();
nostr_relay_connect();

// Relay-side (for relay implementations)
nostr_client_msg_parse();
nostr_filter_matches();
nostr_event_validate_full();
nostr_deletion_authorized();
nostr_relay_msg_serialize();
nostr_relay_info_serialize();

Motivation

Building nostr applications in C requires reliable, efficient cryptographic operations and protocol handling. libnostr-c provides a complete implementation with modern NIP-44 encryption, Lightning integration, and minimal resource requirements. Built with security-first design principles and optimized for both desktop and embedded systems.

Platform Support

The following table lists supported platforms and cryptographic backends:

Platform Crypto Backend Notes CI Status
Linux noscrypt, secp256k1 GCC/Clang ✅ Tested
macOS noscrypt, secp256k1 Clang ✅ Tested
Windows noscrypt, secp256k1 MSVC ✅ Tested
ESP-IDF noscrypt, mbedtls ESP32/S2/S3/C3/C6, v5.0+ ✅ Tested, run under QEMU

ESP-IDF stack requirement

On ESP targets the RNG goes through a cached mbedTLS CTR-DRBG rather than esp_fill_random(), so that a failed draw can be reported instead of silently returning zeros. That costs stack in the task that happens to seed it.

Call nostr_random_bytes(), nostr_key_generate(), nostr_keypair_generate() or any signing entry point from a task with at least 4 KB of stack. (nostr_keypair_generate() delegates to nostr_key_generate(), so either reaches the same draw.) The first call in the process seeds the DRBG, and ctr_drbg_reseed_internal() puts a 384-byte MBEDTLS_CTR_DRBG_MAX_SEED_INPUT buffer on the caller's stack (mbedtls/library/ctr_drbg.c:452), plus the entropy gather. Roughly 500 bytes transient that esp_fill_random() never needed. It is re-paid every MBEDTLS_CTR_DRBG_RESEED_INTERVAL draws, which is 10000 by default, so a task that seeds successfully once can still overflow much later.

Seeding lazily from whichever task draws first means the cost lands wherever that happens to be. Calling nostr_init() early, from a task you control the depth of, makes it predictable.

Fixed cost is small: 500 bytes of .bss for the two contexts, measured from a built ESP32-S3 image (rng_entropy 420 B, rng_ctr_drbg 76 B, rng_initialized 4 B). The entropy accumulator also mallocs its message-digest context on first use and never frees it, which is one-time rather than a growing leak.

This is measured, not estimated. Under QEMU on ESP32-S3:

first caller result
2 KB task drawing from an already-seeded DRBG passes
2 KB task that seeds the DRBG itself crashes, Guru Meditation Error: Core 1 panic'ed (LoadProhibited)
3 KB task that seeds passes
4 KB task that seeds passes
8 KB task (app_main) seeding, then 2 KB tasks drawing passes

The threshold is between 2 KB and 3 KB on this build. 4 KB is the recommendation because it leaves margin for your own frames on top of the seed, not because 3 KB was observed to fail.

So the hazard is specifically the first caller, which is why seeding from a task whose depth you control is worth doing deliberately.

test/esp-idf sets CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192, so CI seeds from app_main with headroom and will not catch a consumer that seeds from a smaller task.

Getting started

Please use the following links to obtain packages and extended documentation.

Documentation
Examples
Performance Analysis

Super quick start

Prerequisites:

  • CMake 3.16+, C99 compiler, OpenSSL
  • secp256k1, libcjson, libwebsockets (see BUILDING.md)
git clone https://github.com/privkeyio/libnostr-c.git
cd libnostr-c
mkdir build && cd build
cmake ..
make -j$(nproc)
sudo make install

For detailed installation instructions, dependency management, and platform-specific setup, see docs/BUILDING.md.

Notes

Builds

Automated builds and releases are available through GitHub Actions CI/CD.

Features

libnostr-c supports modular compilation - build only what you need:

  • Core NIPs (01, 02, 05, 06, 10, 13, 18, 19, 21, 25, 57, 65) enabled by default
  • Optional NIPs (04, 09, 11, 17, 26, 40, 44, 45, 46, 47, 49, 50, 51, 59) via feature flags
  • NIP-11 relay information document for serving relay metadata
  • Relay protocol support for building relay implementations
  • Modern noscrypt backend provides NIP-44 encryption
  • Fallback to secp256k1 for basic operations

Projects Using libnostr-c

  • keep-esp32 - Air-gapped ESP32-S3 FROST threshold signing device with Nostr DKG coordination
  • wisp-esp32 - Minimal ESP32-S3 Nostr relay with 21-day ephemeral storage
  • whisper - Encrypted DM pipe for Nostr using NIP-17 + NIP-44, Unix-style CLI
  • vain - High-performance vanity Nostr public key miner

License

The software in this repository is licensed under MIT. See the LICENSE file for details.

Acknowledgments

This project is powered by noscrypt for high-performance cryptographic operations and NIP-44 encryption.

About

Lightweight, portable C library for the Nostr protocol with native Lightning Network integration.

Topics

Resources

Contributing

Security policy

Stars

12 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages