From 4201e977808ae1ffdc2f64a17e5119905a2a319f Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 14 Aug 2025 15:26:25 -0400 Subject: [PATCH] Initial commit: Generalized C project template with automatic versioning --- .clinerules/workspace_rules.md | 11 + .gitignore | 16 ++ AUTOMATIC_VERSIONING_GUIDE.md | 361 ++++++++++++++++++++++++ README.md | 484 +++++++++++++++++++++++++++++++++ build.sh | 362 ++++++++++++++++++++++++ 5 files changed, 1234 insertions(+) create mode 100644 .clinerules/workspace_rules.md create mode 100644 .gitignore create mode 100644 AUTOMATIC_VERSIONING_GUIDE.md create mode 100644 README.md create mode 100755 build.sh diff --git a/.clinerules/workspace_rules.md b/.clinerules/workspace_rules.md new file mode 100644 index 0000000..cef895f --- /dev/null +++ b/.clinerules/workspace_rules.md @@ -0,0 +1,11 @@ +This library is fully staticly linked. There should be no external dependencies. + +When building, use build.sh, not make. + +Use it as follows: build.sh -m "useful comment on changes being made" + +When making TUI menus, try to use the first leter of the command and the key to press to execute that command. For example, if the command is "Open file" try to use a keypress of "o" upper or lower case to signal to open the file. Use this instead of number keyed menus when possible. In the command, the letter should be underlined that signifies the command. + +When deleting, everything gets moved to the Trash folder. + +MAKEFILE POLICY: There should be only ONE Makefile in the entire project. All build logic (library, tests, examples, websocket) must be consolidated into the root Makefile. Do not create separate Makefiles in subdirectories as this creates testing inconsistencies where you test with one Makefile but run with another. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0be47c9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +Trash/ + +# Auto-generated version files +src/version.h +src/version.c +lib/version.h +lib/version.c +version.h +version.c +VERSION + +*.o +*.a +*.so +*.dylib +*.dll diff --git a/AUTOMATIC_VERSIONING_GUIDE.md b/AUTOMATIC_VERSIONING_GUIDE.md new file mode 100644 index 0000000..c8fcc0a --- /dev/null +++ b/AUTOMATIC_VERSIONING_GUIDE.md @@ -0,0 +1,361 @@ +# Generic Automatic Version Increment System for Any Repository + +Here's a generalized implementation guide for adding automatic versioning to any project: + +## Core Concept +**Automatic patch version increment with each build** - Every build automatically increments the patch version: v0.1.0 → v0.1.1 → v0.1.2, etc. + +## Implementation Steps + +### 1. Add Version Increment Function to Build Script +Add this function to your build script (bash example): + +```bash +# Function to automatically increment version +increment_version() { + echo "[INFO] Incrementing version..." + + # Check if we're in a git repository + if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo "[WARNING] Not in a git repository - skipping version increment" + return 0 + fi + + # Get the highest version tag (not chronologically latest) + LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0") + if [[ -z "$LATEST_TAG" ]]; then + LATEST_TAG="v0.1.0" + fi + + # Extract version components (remove 'v' prefix) + VERSION=${LATEST_TAG#v} + + # Parse major.minor.patch using regex + if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + MAJOR=${BASH_REMATCH[1]} + MINOR=${BASH_REMATCH[2]} + PATCH=${BASH_REMATCH[3]} + else + echo "[ERROR] Invalid version format in tag: $LATEST_TAG" + echo "[ERROR] Expected format: v0.1.0" + return 1 + fi + + # Increment patch version + NEW_PATCH=$((PATCH + 1)) + NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}" + + echo "[INFO] Current version: $LATEST_TAG" + echo "[INFO] New version: $NEW_VERSION" + + # Create new git tag + if git tag "$NEW_VERSION" 2>/dev/null; then + echo "[SUCCESS] Created new version tag: $NEW_VERSION" + else + echo "[WARNING] Tag $NEW_VERSION already exists - using existing version" + NEW_VERSION=$LATEST_TAG + fi + + # Update VERSION file for compatibility + echo "${NEW_VERSION#v}" > VERSION + echo "[SUCCESS] Updated VERSION file to ${NEW_VERSION#v}" +} +``` + +### 2. Generate Version Header Files (For C/C++ Projects) +Add this to the increment_version function: + +```bash +# Generate version.h header file (adjust path as needed) +cat > src/version.h << EOF +/* + * Auto-Generated Version Header + * DO NOT EDIT THIS FILE MANUALLY - Generated by build script + */ + +#ifndef VERSION_H +#define VERSION_H + +#define VERSION_MAJOR ${MAJOR} +#define VERSION_MINOR ${MINOR} +#define VERSION_PATCH ${NEW_PATCH} +#define VERSION_STRING "${MAJOR}.${MINOR}.${NEW_PATCH}" +#define VERSION_TAG "${NEW_VERSION}" + +/* Build information */ +#define BUILD_DATE "$(date +%Y-%m-%d)" +#define BUILD_TIME "$(date +%H:%M:%S)" +#define BUILD_TIMESTAMP "$(date '+%Y-%m-%d %H:%M:%S')" + +/* Git information */ +#define GIT_HASH "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" +#define GIT_BRANCH "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')" + +/* Display versions */ +#define VERSION_DISPLAY "${NEW_VERSION}" +#define VERSION_FULL_DISPLAY "${NEW_VERSION} ($(date '+%Y-%m-%d %H:%M:%S'), $(git rev-parse --short HEAD 2>/dev/null || echo 'unknown'))" + +/* Version API functions */ +const char* get_version(void); +const char* get_version_full(void); +const char* get_build_info(void); + +#endif /* VERSION_H */ +EOF + +# Generate version.c implementation file +cat > src/version.c << EOF +/* + * Auto-Generated Version Implementation + * DO NOT EDIT THIS FILE MANUALLY - Generated by build script + */ + +#include "version.h" + +const char* get_version(void) { + return VERSION_TAG; +} + +const char* get_version_full(void) { + return VERSION_FULL_DISPLAY; +} + +const char* get_build_info(void) { + return "Built on " BUILD_DATE " at " BUILD_TIME " from commit " GIT_HASH " on branch " GIT_BRANCH; +} +EOF +``` + +### 3. Generate Version File for Other Languages + +**Python (`src/__version__.py`):** +```bash +cat > src/__version__.py << EOF +"""Auto-generated version file""" +__version__ = "${MAJOR}.${MINOR}.${NEW_PATCH}" +__version_tag__ = "${NEW_VERSION}" +__build_date__ = "$(date +%Y-%m-%d)" +__build_time__ = "$(date +%H:%M:%S)" +__git_hash__ = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" +__git_branch__ = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')" +EOF +``` + +**JavaScript/Node.js (update `package.json`):** +```bash +# Update package.json version field +if [ -f package.json ]; then + sed -i "s/\"version\": \".*\"/\"version\": \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" package.json +fi +``` + +**Rust (update `Cargo.toml`):** +```bash +if [ -f Cargo.toml ]; then + sed -i "s/^version = \".*\"/version = \"${MAJOR}.${MINOR}.${NEW_PATCH}\"/" Cargo.toml +fi +``` + +**Go (generate `version.go`):** +```bash +cat > version.go << EOF +// Auto-generated version file +package main + +const ( + VersionMajor = ${MAJOR} + VersionMinor = ${MINOR} + VersionPatch = ${NEW_PATCH} + VersionString = "${MAJOR}.${MINOR}.${NEW_PATCH}" + VersionTag = "${NEW_VERSION}" + BuildDate = "$(date +%Y-%m-%d)" + BuildTime = "$(date +%H:%M:%S)" + GitHash = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" + GitBranch = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')" +) +EOF +``` + +**Java (generate `Version.java`):** +```bash +cat > src/main/java/Version.java << EOF +// Auto-generated version class +public class Version { + public static final int VERSION_MAJOR = ${MAJOR}; + public static final int VERSION_MINOR = ${MINOR}; + public static final int VERSION_PATCH = ${NEW_PATCH}; + public static final String VERSION_STRING = "${MAJOR}.${MINOR}.${NEW_PATCH}"; + public static final String VERSION_TAG = "${NEW_VERSION}"; + public static final String BUILD_DATE = "$(date +%Y-%m-%d)"; + public static final String BUILD_TIME = "$(date +%H:%M:%S)"; + public static final String GIT_HASH = "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"; + public static final String GIT_BRANCH = "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')"; +} +EOF +``` + +### 4. Integrate into Build Targets +Call `increment_version` before your main build commands: + +```bash +build_library() { + increment_version + echo "[INFO] Building library..." + # Your actual build commands here + make clean && make +} + +build_release() { + increment_version + echo "[INFO] Building release..." + # Your release build commands +} + +build_package() { + increment_version + echo "[INFO] Building package..." + # Your packaging commands +} +``` + +### 5. Update .gitignore +Add generated version files to `.gitignore`: + +```gitignore +# Auto-generated version files +src/version.h +src/version.c +src/__version__.py +version.go +src/main/java/Version.java +VERSION +``` + +### 6. Update Build System Files + +**For Makefile projects:** +```makefile +# Add version.c to your source files +SOURCES = main.c utils.c version.c +``` + +**For CMake projects:** +```cmake +# Add version files to your target +target_sources(your_target PRIVATE src/version.c) +``` + +**For Node.js projects:** +```json +{ + "scripts": { + "build": "node build.js && increment_version", + "version": "node -e \"console.log(require('./package.json').version)\"" + } +} +``` + +### 7. Create Initial Version Tag +```bash +# Start with initial version +git tag v0.1.0 +``` + +## Usage Pattern +```bash +./build.sh # v0.1.0 → v0.1.1 +./build.sh release # v0.1.1 → v0.1.2 +./build.sh package # v0.1.2 → v0.1.3 +``` + +## Manual Version Control + +### Major/Minor Version Bumps +```bash +# For feature releases (minor bump) +git tag v0.2.0 # Next build: v0.2.1 + +# For breaking changes (major bump) +git tag v1.0.0 # Next build: v1.0.1 +``` + +### Version Reset +```bash +# Delete incorrect tags (if needed) +git tag -d v0.2.1 +git push origin --delete v0.2.1 # If pushed to remote + +# Create correct base version +git tag v0.2.0 + +# Next build will create v0.2.1 +``` + +## Example Build Script Template +```bash +#!/bin/bash +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +print_status() { echo -e "${BLUE}[INFO]${NC} $1"; } +print_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; } +print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; } +print_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# Insert increment_version function here + +case "${1:-build}" in + build) + increment_version + print_status "Building project..." + # Your build commands + ;; + clean) + print_status "Cleaning build artifacts..." + # Your clean commands + ;; + test) + print_status "Running tests..." + # Your test commands (no version increment) + ;; + release) + increment_version + print_status "Building release..." + # Your release commands + ;; + *) + echo "Usage: $0 {build|clean|test|release}" + exit 1 + ;; +esac +``` + +## Benefits +1. **Zero maintenance** - No manual version editing +2. **Build traceability** - Every build has unique version + metadata +3. **Git integration** - Automatic version tags +4. **Language agnostic** - Adapt generation for any language +5. **CI/CD friendly** - Works in automated environments +6. **Rollback friendly** - Easy to revert to previous versions + +## Troubleshooting + +### Version Not Incrementing +- Ensure you're in a git repository +- Check that git tags exist: `git tag --list` +- Verify tag format matches `v*.*.*` pattern + +### Tag Already Exists +If a tag already exists, the build continues with existing version: +``` +[WARNING] Tag v0.2.1 already exists - using existing version +``` + +### Missing Git Information +If git is unavailable, version files show "unknown" for git hash and branch. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d86a2b1 --- /dev/null +++ b/README.md @@ -0,0 +1,484 @@ +# NOSTR Core Library + +A comprehensive, production-ready C library for NOSTR protocol implementation with OpenSSL-based cryptography and extensive protocol support. + +[![Version](https://img.shields.io/badge/version-0.1.20-blue.svg)](VERSION) +[![License](https://img.shields.io/badge/license-MIT-green.svg)](#license) +[![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](#building) + +## 🚀 Features + +### Core Protocol Support +- **NIP-01**: Basic protocol flow - event creation, signing, and validation +- **NIP-04**: Encrypted direct messages (ECDH + AES-CBC + Base64) +- **NIP-05**: DNS-based internet identifier verification +- **NIP-06**: Key derivation from mnemonic (BIP39/BIP32 compliant) +- **NIP-11**: Relay information documents +- **NIP-13**: Proof of Work for events +- **NIP-44**: Versioned encrypted direct messages (ECDH + ChaCha20 + HMAC) + +### Cryptographic Features +- **OpenSSL-Based**: Production-grade cryptography with OpenSSL backend +- **Secp256k1**: Complete elliptic curve implementation bundled +- **BIP39**: Mnemonic phrase generation and validation +- **BIP32**: Hierarchical deterministic key derivation +- **ChaCha20**: Stream cipher for NIP-44 encryption +- **AES-CBC**: Block cipher for NIP-04 encryption +- **Schnorr Signatures**: BIP-340 compliant signing and verification + +### Networking & Relay Support +- **Multi-Relay Queries**: Synchronous querying with progress callbacks +- **Relay Pools**: Asynchronous connection management with statistics +- **OpenSSL WebSocket Communication**: Full relay protocol support with TLS +- **NIP-05 Identifier Verification**: DNS-based identity resolution +- **NIP-11 Relay Information**: Automatic relay capability discovery +- **Event Deduplication**: Automatic handling of duplicate events across relays +- **Connection Management**: Automatic reconnection and error handling + +### Developer Experience +- **Minimal Dependencies**: Only requires OpenSSL, standard C library and math library +- **Thread-Safe**: Core cryptographic functions are stateless +- **Cross-Platform**: Builds on Linux, macOS, Windows +- **Comprehensive Examples**: Ready-to-run demonstration programs +- **Automatic Versioning**: Git-tag based version management + +## 📦 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 lib + ``` + +3. **Run examples:** + ```bash + ./build.sh examples + ./examples/simple_keygen + ``` + +### Usage Example + +```c +#include "nostr_core/nostr_core.h" +#include + +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.a -lm +./example +``` + +## 🏗️ Building + +### Build Targets + +```bash +./build.sh lib # Build static library (default) +./build.sh examples # Build examples +./build.sh test # Run test suite +./build.sh clean # Clean build artifacts +./build.sh install # Install to system +``` + +### Manual Building + +```bash +# Build static library +make + +# Build examples +make examples + +# Run tests +make test-crypto + +# Clean +make clean +``` + +### Dependencies + +**Required:** +- GCC or compatible C compiler +- Standard C library +- Math library (`-lm`) + +**Included & Embedded (x64):** +- cJSON (JSON parsing) +- secp256k1 (elliptic curve cryptography) +- OpenSSL (complete cryptographic backend + TLS) +- curl (HTTP/HTTPS for NIP-05/NIP-11) + +**ARM64 Additional Requirements:** +- System OpenSSL libraries (`-lssl -lcrypto`) + +## 📚 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 +nostr_relay_pool_t* nostr_relay_pool_create(void); +int nostr_relay_pool_add_relay(nostr_relay_pool_t* pool, const char* relay_url); +void nostr_relay_pool_destroy(nostr_relay_pool_t* pool); + +// Subscribe to events +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); + +// Run event loop +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); +``` + +### 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); +``` + +## 📁 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 +- **`version_test`** - Library version information + +Run all examples: +```bash +./build.sh examples +ls -la examples/ +``` + +## 🧪 Testing + +The library includes extensive tests: + +```bash +# Run all tests +./build.sh test + +# Individual test categories +cd tests && make test +``` + +**Test Categories:** +- **Core Functionality**: `simple_init_test`, `header_test` +- **Cryptography**: `chacha20_test`, `nostr_crypto_test` +- **NIP-04 Encryption**: `nip04_test` +- **NIP-05 Identifiers**: `nip05_test` +- **NIP-11 Relay Info**: `nip11_test` +- **NIP-44 Encryption**: `nip44_test`, `nip44_debug_test` +- **Key Derivation**: `nostr_test_bip32` +- **Relay Communication**: `relay_pool_test`, `sync_test` +- **HTTP/WebSocket**: `http_test`, `wss_test` +- **Proof of Work**: `test_pow_loop` + +## 🏗️ Integration + +### Static Library Integration + +1. **Copy required files to your project:** + ```bash + cp libnostr_core.a /path/to/your/project/ + cp nostr_core/nostr_core.h /path/to/your/project/ + ``` + +2. **Link in your project:** + ```bash + gcc your_code.c -L. -lnostr_core -lm -o your_program + ``` + +### Source Integration + +1. **Copy source files:** + ```bash + cp -r nostr_core/ /path/to/your/project/ + cp -r cjson/ /path/to/your/project/ + ``` + +2. **Include in your build:** + ```bash + gcc your_code.c nostr_core/*.c cjson/cJSON.c -lm -o your_program + ``` + +### Self-Contained Library + +**x64 Library:** The `libnostr_core.a` file is completely self-contained with **no external dependencies**: + +- ✅ **All OpenSSL code embedded** +- ✅ **No libwally required** +- ✅ **No system secp256k1 required** +- ✅ **Only needs math library (`-lm`)** + +```bash +# x64 - This is all you need: +gcc your_app.c ./libnostr_core.a -lm -o your_app +``` + +**ARM64 Library:** The `libnostr_core_arm64.a` requires system OpenSSL: + +```bash +# ARM64 - Requires OpenSSL libraries: +aarch64-linux-gnu-gcc your_app.c ./libnostr_core_arm64.a -lssl -lcrypto -lm -o your_app +``` + +## 🔧 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 all logging +make LOGGING_FLAGS="-DENABLE_FILE_LOGGING -DENABLE_WEBSOCKET_LOGGING -DENABLE_DEBUG_LOGGING" + +# Debug build +make debug + +# ARM64 cross-compile +make arm64 +``` + +## 🌐 Supported Platforms + +- **Linux** (x86_64, ARM64) +- **macOS** (Intel, Apple Silicon) +- **Windows** (MinGW, MSYS2) +- **Embedded Systems** (resource-constrained environments) + +## 📄 Documentation + +- **[LIBRARY_USAGE.md](LIBRARY_USAGE.md)** - Detailed integration guide +- **[EXPORT_GUIDE.md](EXPORT_GUIDE.md)** - Library export instructions +- **[AUTOMATIC_VERSIONING.md](AUTOMATIC_VERSIONING.md)** - Version management +- **API Reference** - Complete documentation in `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. Run the test suite: `./build.sh 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.1.20** + +The library uses automatic semantic versioning based on Git tags. Each build increments the patch version automatically. + +**Recent Developments:** +- **OpenSSL Migration**: Transitioned from mbedTLS to OpenSSL for improved compatibility +- **NIP-05 Support**: DNS-based internet identifier verification +- **NIP-11 Support**: Relay information document fetching and parsing +- **Enhanced WebSocket**: OpenSSL-based TLS WebSocket communication +- **Production Ready**: Comprehensive test suite and error handling + +**Version Timeline:** +- `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-44 support + +## 🐛 Troubleshooting + +### Common Issues + +**Build fails with secp256k1 errors:** +```bash +cd secp256k1 +./autogen.sh +./configure --enable-module-schnorrsig --enable-module-ecdh +make +cd .. +./build.sh lib +``` + +**Library too large:** +The x64 library is intentionally large (~15MB) because it includes all secp256k1 cryptographic functions and OpenSSL for complete self-containment. The ARM64 library is smaller (~2.4MB) as it links against system OpenSSL. + +**Linking errors:** +Make sure to include the math library: +```bash +gcc your_code.c ./libnostr_core.a -lm # Note the -lm flag +``` + +### Getting Help + +- Check the `examples/` directory for working code +- Run `./build.sh test` 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 • Production ready* diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..d707008 --- /dev/null +++ b/build.sh @@ -0,0 +1,362 @@ +#!/bin/bash + +# Generic C Project Build Script +# Provides convenient build targets with automatic version management +# Automatically increments patch version with each build + +set -e # Exit on any error + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_status() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Detect project name and configuration +detect_project_config() { + # Use environment variable if set, otherwise derive from directory name + if [[ -n "$PROJECT_NAME" ]]; then + PROJECT_NAME_LOWER="$PROJECT_NAME" + else + PROJECT_NAME_LOWER=$(basename "$(pwd)") + fi + + PROJECT_NAME_UPPER=$(echo "$PROJECT_NAME_LOWER" | tr '[:lower:]' '[:upper:]') + PROJECT_NAME_CLEAN=$(echo "$PROJECT_NAME_LOWER" | sed 's/[^a-zA-Z0-9_]/_/g') + + # Detect source directory + if [[ -d "src" ]]; then + SRC_DIR="src" + elif [[ -d "lib" ]]; then + SRC_DIR="lib" + elif [[ -d "$PROJECT_NAME_LOWER" ]]; then + SRC_DIR="$PROJECT_NAME_LOWER" + else + SRC_DIR="." + fi + + # Set library name + LIB_NAME="lib${PROJECT_NAME_CLEAN}.a" +} + +# Function to automatically increment version +increment_version() { + print_status "Incrementing version..." + + # Check if we're in a git repository + if ! git rev-parse --git-dir > /dev/null 2>&1; then + print_warning "Not in a git repository - skipping version increment" + return 0 + fi + + # Get the highest version tag (not necessarily the most recent chronologically) + LATEST_TAG=$(git tag -l 'v*.*.*' | sort -V | tail -n 1 || echo "v0.1.0") + if [[ -z "$LATEST_TAG" ]]; then + LATEST_TAG="v0.1.0" + fi + + # Extract version components (remove 'v' prefix if present) + VERSION=${LATEST_TAG#v} + + # Parse major.minor.patch + if [[ $VERSION =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + MAJOR=${BASH_REMATCH[1]} + MINOR=${BASH_REMATCH[2]} + PATCH=${BASH_REMATCH[3]} + else + print_error "Invalid version format in tag: $LATEST_TAG" + print_error "Expected format: v0.1.0" + return 1 + fi + + # Increment patch version + NEW_PATCH=$((PATCH + 1)) + NEW_VERSION="v${MAJOR}.${MINOR}.${NEW_PATCH}" + + print_status "Current version: $LATEST_TAG" + print_status "New version: $NEW_VERSION" + + # Create new git tag + if git tag "$NEW_VERSION" 2>/dev/null; then + print_success "Created new version tag: $NEW_VERSION" + + # Generate version.h header file + cat > "${SRC_DIR}/version.h" << EOF +/* + * ${PROJECT_NAME_UPPER} - Auto-Generated Version Header + * DO NOT EDIT THIS FILE MANUALLY - Generated by build script + */ + +#ifndef ${PROJECT_NAME_UPPER}_VERSION_H +#define ${PROJECT_NAME_UPPER}_VERSION_H + +#define VERSION_MAJOR ${MAJOR} +#define VERSION_MINOR ${MINOR} +#define VERSION_PATCH ${NEW_PATCH} +#define VERSION_STRING "${MAJOR}.${MINOR}.${NEW_PATCH}" +#define VERSION_TAG "${NEW_VERSION}" + +/* Build information */ +#define BUILD_DATE "$(date +%Y-%m-%d)" +#define BUILD_TIME "$(date +%H:%M:%S)" +#define BUILD_TIMESTAMP "$(date '+%Y-%m-%d %H:%M:%S')" + +/* Git information */ +#define GIT_HASH "$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')" +#define GIT_BRANCH "$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo 'unknown')" + +/* Display versions */ +#define VERSION_DISPLAY "${NEW_VERSION}" +#define VERSION_FULL_DISPLAY "${NEW_VERSION} ($(date '+%Y-%m-%d %H:%M:%S'), $(git rev-parse --short HEAD 2>/dev/null || echo 'unknown'))" + +/* Version API functions */ +const char* get_version(void); +const char* get_version_full(void); +const char* get_build_info(void); + +#endif /* ${PROJECT_NAME_UPPER}_VERSION_H */ +EOF + + # Generate version.c implementation file + cat > "${SRC_DIR}/version.c" << EOF +/* + * ${PROJECT_NAME_UPPER} - Auto-Generated Version Implementation + * DO NOT EDIT THIS FILE MANUALLY - Generated by build script + */ + +#include "version.h" + +const char* get_version(void) { + return VERSION_TAG; +} + +const char* get_version_full(void) { + return VERSION_FULL_DISPLAY; +} + +const char* get_build_info(void) { + return "Built on " BUILD_DATE " at " BUILD_TIME " from commit " GIT_HASH " on branch " GIT_BRANCH; +} +EOF + + print_success "Generated version.h and version.c in ${SRC_DIR}/" + else + print_warning "Tag $NEW_VERSION already exists - using existing version" + NEW_VERSION=$LATEST_TAG + fi + + # Update VERSION file for compatibility + echo "${NEW_VERSION#v}" > VERSION + print_success "Updated VERSION file to ${NEW_VERSION#v}" +} + +# Function to show usage +show_usage() { + echo "${PROJECT_NAME_UPPER} Build Script" + echo "$(printf '=%.0s' $(seq 1 $((${#PROJECT_NAME_UPPER} + 13))))" + echo "" + echo "Usage: $0 [target] [-m \"commit message\"]" + echo "" + echo "Available targets:" + echo " clean - Clean all build artifacts" + echo " build - Build the project (default)" + echo " lib - Build static library" + echo " examples - Build example programs (if examples/ exists)" + echo " test - Run tests (if tests exist)" + echo " install - Install library to system" + echo " uninstall - Remove library from system" + echo " help - Show this help message" + echo "" + echo "Options:" + echo " -m \"message\" - Commit message (required by workspace rules)" + echo "" + echo "Library output: $LIB_NAME" + echo "Source directory: $SRC_DIR/" + echo "" + echo "Note: Each build automatically increments the patch version." +} + +# Parse command line arguments +COMMIT_MESSAGE="" +TARGET="" + +while [[ $# -gt 0 ]]; do + case $1 in + -m) + COMMIT_MESSAGE="$2" + shift 2 + ;; + clean|build|lib|examples|test|install|uninstall|help|--help|-h) + TARGET="$1" + shift + ;; + *) + if [[ -z "$TARGET" ]]; then + TARGET="$1" + else + print_error "Unknown option: $1" + show_usage + exit 1 + fi + shift + ;; + esac +done + +# Set default target +TARGET=${TARGET:-build} + +# Detect project configuration +detect_project_config + +# Handle targets +case "$TARGET" in + clean) + print_status "Cleaning build artifacts..." + if [[ -f "Makefile" ]]; then + make clean + else + rm -f *.a *.so *.o + rm -f "${SRC_DIR}/version.h" "${SRC_DIR}/version.c" + fi + print_success "Clean completed" + ;; + + build|lib) + if [[ -n "$COMMIT_MESSAGE" ]]; then + print_status "Build message: $COMMIT_MESSAGE" + fi + + increment_version + print_status "Building $PROJECT_NAME_UPPER..." + + if [[ -f "Makefile" ]]; then + make clean + make + else + print_error "No Makefile found. Please create a Makefile for your project." + print_error "The build script works as a wrapper around make." + exit 1 + fi + + # Check if library was built + if [[ -f "$LIB_NAME" ]] || [[ -f "lib${PROJECT_NAME_CLEAN}.so" ]]; then + if [[ -f "$LIB_NAME" ]]; then + SIZE=$(stat -c%s "$LIB_NAME" 2>/dev/null || stat -f%z "$LIB_NAME" 2>/dev/null || echo "unknown") + print_success "Static library built successfully (${SIZE} bytes)" + ls -la "$LIB_NAME" + fi + if [[ -f "lib${PROJECT_NAME_CLEAN}.so" ]]; then + SIZE=$(stat -c%s "lib${PROJECT_NAME_CLEAN}.so" 2>/dev/null || stat -f%z "lib${PROJECT_NAME_CLEAN}.so" 2>/dev/null || echo "unknown") + print_success "Shared library built successfully (${SIZE} bytes)" + ls -la "lib${PROJECT_NAME_CLEAN}.so" + fi + else + print_warning "No library file found. Check your Makefile configuration." + fi + ;; + + examples) + if [[ ! -d "examples" ]]; then + print_error "No examples/ directory found" + exit 1 + fi + + increment_version + print_status "Building examples..." + + if [[ -f "Makefile" ]]; then + make clean + make + if make examples 2>/dev/null; then + print_success "Examples built successfully" + ls -la examples/ + else + print_warning "Examples target not found in Makefile" + fi + else + print_error "No Makefile found for building examples" + exit 1 + fi + ;; + + test) + print_status "Running tests..." + + if [[ -f "Makefile" ]]; then + make clean + make + if make test 2>/dev/null; then + print_success "All tests passed" + else + print_warning "Test target not found in Makefile, checking tests/ directory" + if [[ -d "tests" ]]; then + cd tests && make test 2>/dev/null && cd .. + print_success "Tests completed" + else + print_error "No tests found" + exit 1 + fi + fi + else + print_error "No Makefile found for running tests" + exit 1 + fi + ;; + + install) + increment_version + print_status "Installing library to system..." + + if [[ -f "Makefile" ]]; then + make clean + make + sudo make install + print_success "Library installed to /usr/local" + else + print_error "No Makefile found for installation" + exit 1 + fi + ;; + + uninstall) + print_status "Uninstalling library from system..." + + if [[ -f "Makefile" ]]; then + sudo make uninstall + print_success "Library uninstalled" + else + print_error "No Makefile found for uninstallation" + exit 1 + fi + ;; + + help|--help|-h) + show_usage + ;; + + *) + print_error "Unknown target: $TARGET" + echo "" + show_usage + exit 1 + ;; +esac