785 lines
32 KiB
Markdown
785 lines
32 KiB
Markdown
# NOSTR Core Library
|
||
|
||
A C library for NOSTR protocol implementation. Work in progress.
|
||
|
||
[](VERSION)
|
||
[](#license)
|
||
[](#building)
|
||
|
||
|
||
## 📋 NIP Implementation Status
|
||
|
||
### Core Protocol NIPs
|
||
- [x] [NIP-01](nips/01.md) - Basic protocol flow - event creation, signing, and validation
|
||
- [ ] [NIP-02](nips/02.md) - Contact List and Petnames
|
||
- [x] [NIP-03](nips/03.md) - OpenTimestamps Attestations for Events
|
||
- [x] [NIP-04](nips/04.md) - Encrypted Direct Messages (legacy)
|
||
- [x] [NIP-05](nips/05.md) - Mapping Nostr keys to DNS-based internet identifiers
|
||
- [x] [NIP-06](nips/06.md) - Basic key derivation from mnemonic seed phrase
|
||
- [-] [NIP-07](nips/07.md) - `window.nostr` capability for web browsers
|
||
- [ ] [NIP-08](nips/08.md) - Handling Mentions
|
||
- [ ] [NIP-09](nips/09.md) - Event Deletion
|
||
- [ ] [NIP-10](nips/10.md) - Conventions for clients' use of `e` and `p` tags in text events
|
||
- [x] [NIP-11](nips/11.md) - Relay Information Document
|
||
- [ ] [NIP-12](nips/12.md) - Generic Tag Queries
|
||
- [x] [NIP-13](nips/13.md) - Proof of Work
|
||
- [ ] [NIP-14](nips/14.md) - Subject tag in text events
|
||
- [ ] [NIP-15](nips/15.md) - Nostr Marketplace (for resilient marketplaces)
|
||
- [ ] [NIP-16](nips/16.md) - Event Treatment
|
||
- [x] [NIP-17](nips/17.md) - Private Direct Messages
|
||
- [ ] [NIP-18](nips/18.md) - Reposts
|
||
- [x] [NIP-19](nips/19.md) - bech32-encoded entities
|
||
- [ ] [NIP-20](nips/20.md) - Command Results
|
||
- [x] [NIP-21](nips/21.md) - `nostr:` URI scheme
|
||
- [ ] [NIP-22](nips/22.md) - Event `created_at` Limits
|
||
- [ ] [NIP-23](nips/23.md) - Long-form Content
|
||
- [ ] [NIP-24](nips/24.md) - Extra metadata fields and tags
|
||
- [ ] [NIP-25](nips/25.md) - Reactions
|
||
- [ ] [NIP-26](nips/26.md) - Delegated Event Signing
|
||
- [ ] [NIP-27](nips/27.md) - Text Note References
|
||
- [ ] [NIP-28](nips/28.md) - Public Chat
|
||
- [ ] [NIP-29](nips/29.md) - Relay-based Groups
|
||
- [ ] [NIP-30](nips/30.md) - Custom Emoji
|
||
- [ ] [NIP-31](nips/31.md) - Dealing with Unknown Events
|
||
- [ ] [NIP-32](nips/32.md) - Labeling
|
||
- [ ] [NIP-33](nips/33.md) - Parameterized Replaceable Events
|
||
- [ ] [NIP-34](nips/34.md) - `git` stuff
|
||
- [ ] [NIP-35](nips/35.md) - Torrents
|
||
- [ ] [NIP-36](nips/36.md) - Sensitive Content / Content Warning
|
||
- [ ] [NIP-37](nips/37.md) - Draft Events
|
||
- [ ] [NIP-38](nips/38.md) - User Statuses
|
||
- [ ] [NIP-39](nips/39.md) - External Identities in Profiles
|
||
- [ ] [NIP-40](nips/40.md) - Expiration Timestamp
|
||
- [x] [NIP-42](nips/42.md) - Authentication of clients to relays
|
||
- [x] [NIP-44](nips/44.md) - Versioned Encryption
|
||
- [ ] [NIP-45](nips/45.md) - Counting results
|
||
- [x] [NIP-46](nips/46.md) - Nostr Remote Signing
|
||
- [ ] [NIP-47](nips/47.md) - Wallet Connect
|
||
- [ ] [NIP-48](nips/48.md) - Proxy Tags
|
||
- [ ] [NIP-49](nips/49.md) - Private Key Encryption
|
||
- [ ] [NIP-50](nips/50.md) - Search Capability
|
||
- [ ] [NIP-51](nips/51.md) - Lists
|
||
- [ ] [NIP-52](nips/52.md) - Calendar Events
|
||
- [ ] [NIP-53](nips/53.md) - Live Activities
|
||
- [ ] [NIP-54](nips/54.md) - Wiki
|
||
- [-] [NIP-55](nips/55.md) - Android Signer Application
|
||
- [ ] [NIP-56](nips/56.md) - Reporting
|
||
- [ ] [NIP-57](nips/57.md) - Lightning Zaps
|
||
- [ ] [NIP-58](nips/58.md) - Badges
|
||
- [x] [NIP-59](nips/59.md) - Gift Wrap
|
||
- [ ] [NIP-60](nips/60.md) - Cashu Wallet
|
||
- [ ] [NIP-61](nips/61.md) - Nutzaps
|
||
- [ ] [NIP-62](nips/62.md) - Log events
|
||
- [-] [NIP-64](nips/64.md) - Chess (PGN)
|
||
- [ ] [NIP-65](nips/65.md) - Relay List Metadata
|
||
- [ ] [NIP-66](nips/66.md) - Relay Monitor
|
||
- [-] [NIP-68](nips/68.md) - Web badges
|
||
- [ ] [NIP-69](nips/69.md) - Peer-to-peer Order events
|
||
- [ ] [NIP-70](nips/70.md) - Protected Events
|
||
- [ ] [NIP-71](nips/71.md) - Video Events
|
||
- [ ] [NIP-72](nips/72.md) - Moderated Communities
|
||
- [ ] [NIP-73](nips/73.md) - External Content IDs
|
||
- [ ] [NIP-75](nips/75.md) - Zap Goals
|
||
- [ ] [NIP-77](nips/77.md) - Arbitrary custom app data
|
||
- [ ] [NIP-78](nips/78.md) - Application-specific data
|
||
- [ ] [NIP-84](nips/84.md) - Highlights
|
||
- [ ] [NIP-86](nips/86.md) - Relay Management API
|
||
- [ ] [NIP-87](nips/87.md) - Relay List Recommendations
|
||
- [-] [NIP-88](nips/88.md) - Stella: A Stellar Relay
|
||
- [ ] [NIP-89](nips/89.md) - Recommended Application Handlers
|
||
- [ ] [NIP-90](nips/90.md) - Data Vending Machines
|
||
- [ ] [NIP-92](nips/92.md) - Media Attachments
|
||
- [ ] [NIP-94](nips/94.md) - File Metadata
|
||
- [ ] [NIP-96](nips/96.md) - HTTP File Storage Integration
|
||
- [ ] [NIP-98](nips/98.md) - HTTP Auth
|
||
- [ ] [NIP-99](nips/99.md) - 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:**
|
||
```bash
|
||
git clone https://github.com/yourusername/nostr_core_lib.git
|
||
cd nostr_core_lib
|
||
```
|
||
|
||
2. **Build the library:**
|
||
```bash
|
||
./build.sh
|
||
```
|
||
|
||
3. **Build and run examples:**
|
||
```bash
|
||
./build.sh -e
|
||
./examples/simple_keygen
|
||
```
|
||
|
||
### Usage Example
|
||
|
||
```c
|
||
#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:**
|
||
```bash
|
||
gcc example.c -o example ./libnostr_core_x64.a -lz -ldl -lpthread -lm -lssl -lcrypto -lcurl -lsecp256k1
|
||
./example
|
||
```
|
||
|
||
## 🏗️ Building
|
||
|
||
### Build Targets
|
||
|
||
```bash
|
||
./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:**
|
||
```bash
|
||
sudo apt install build-essential libssl-dev libcurl4-openssl-dev libsecp256k1-dev
|
||
```
|
||
|
||
**Install on CentOS/RHEL:**
|
||
```bash
|
||
sudo yum install gcc openssl-devel libcurl-devel libsecp256k1-devel
|
||
```
|
||
|
||
**Install on macOS:**
|
||
```bash
|
||
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
|
||
```c
|
||
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
|
||
```c
|
||
// 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
|
||
```c
|
||
// 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)
|
||
```c
|
||
// 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
|
||
```c
|
||
// 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)
|
||
```c
|
||
// 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
|
||
```c
|
||
// 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
|
||
```c
|
||
// 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)
|
||
|
||
```c
|
||
/* 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):
|
||
|
||
```c
|
||
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:
|
||
|
||
```c
|
||
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_signer’s 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-03 OpenTimestamps (`nostr_core/nip003.h`)
|
||
- `cJSON* nostr_nip03_create_proof_event_with_signer(const char* target_event_id, int target_event_kind, const char* ots_data_base64, const char* relay_url, nostr_signer_t* signer);`
|
||
- `char* nostr_nip03_request_timestamp(const char* event_id_hex, const char* calendar_url, int timeout_seconds);`
|
||
- `int nostr_nip03_is_proof_complete(const char* ots_data_base64);`
|
||
- `int nostr_nip03_get_attestation_info(const char* ots_data_base64, int* has_bitcoin, int* has_litecoin, int* has_pending, uint64_t* bitcoin_height, uint64_t* litecoin_height, char** pending_uri_out);`
|
||
- `char* nostr_nip03_upgrade_proof(const char* ots_data_base64, const char* calendar_url, int timeout_seconds);`
|
||
- `int nostr_nip03_verify_proof(const char* ots_data_base64, const char* target_digest_hex, uint64_t* block_height_out, time_t* block_time_out);`
|
||
|
||
- 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:
|
||
|
||
```bash
|
||
./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.
|
||
|
||
```bash
|
||
./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:
|
||
```bash
|
||
./build.sh -e
|
||
ls -la examples/
|
||
```
|
||
|
||
## 🧪 Testing
|
||
|
||
The library includes extensive tests:
|
||
|
||
```bash
|
||
# 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/`](tests/) and are generated from `*_test.c` sources.**
|
||
|
||
## 🏗️ Integration
|
||
|
||
### Static Library Integration (Recommended)
|
||
|
||
1. **Install system dependencies first** (see Dependencies section above)
|
||
|
||
2. **Copy required files to your project:**
|
||
```bash
|
||
cp libnostr_core_x64.a /path/to/your/project/
|
||
cp nostr_core/nostr_core.h /path/to/your/project/
|
||
```
|
||
|
||
3. **Link in your project:**
|
||
```bash
|
||
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:**
|
||
```bash
|
||
cp -r nostr_core/ /path/to/your/project/
|
||
cp -r cjson/ /path/to/your/project/
|
||
```
|
||
|
||
3. **Include in your build:**
|
||
```bash
|
||
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:**
|
||
```bash
|
||
gcc your_app.c ./libnostr_core_x64.a -lz -ldl -lpthread -lm -lssl -lcrypto -lcurl -lsecp256k1 -o your_app
|
||
```
|
||
|
||
**Check system dependencies:**
|
||
```bash
|
||
ldd your_app # Shows linked system libraries
|
||
```
|
||
|
||
## 🔧 Configuration
|
||
|
||
### Compile-Time Options
|
||
|
||
```c
|
||
// 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
|
||
|
||
```bash
|
||
# 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.
|
||
|
||
- API is exposed via [`nostr_core/nostr_log.h`](nostr_core/nostr_log.h)
|
||
- Included automatically from [`nostr_core/nostr_core.h`](nostr_core/nostr_core.h)
|
||
|
||
```c
|
||
#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
|
||
|
||
- Direct file logging to `debug.log` has been removed from websocket and NIP-13 internals.
|
||
- To receive logs, register a callback with [`nostr_set_log_callback()`](nostr_core/nostr_log.h:24).
|
||
- Use [`nostr_set_log_level()`](nostr_core/nostr_log.h:25) to reduce verbosity in production.
|
||
|
||
## 🌐 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
|
||
|
||
- **[POOL_API.md](POOL_API.md)** - Relay pool API notes
|
||
- **[nostr_websocket/README.md](nostr_websocket/README.md)** - WebSocket module details
|
||
- **[nostr_websocket/EXPORT_GUIDE.md](nostr_websocket/EXPORT_GUIDE.md)** - WebSocket export instructions
|
||
- **API Reference** - Complete documentation in [`nostr_core/nostr_core.h`](nostr_core/nostr_core.h)
|
||
|
||
## 🤝 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:**
|
||
```bash
|
||
# 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:
|
||
```bash
|
||
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](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*
|