Official Python client SDK for NeoCore API authentication using Ed25519 signatures
Project description
NeoCore Security SDK
Official Python client SDK for NeoCore API authentication using Ed25519 digital signatures.
๐ Overview
This SDK provides banking-grade security for NeoCore API authentication using Ed25519 asymmetric cryptography. All requests are automatically signed with your private key, and the server verifies signatures using your registered public key.
Key Features
- โ Ed25519 Digital Signatures - Industry-standard asymmetric cryptography
- โ Automatic Request Signing - Zero-configuration signing for all HTTP methods
- โ Smart Key Discovery - OS-specific default locations for private keys
- โ Replay Attack Protection - Timestamp and nonce validation
- โ Zero-Trust Architecture - Private key never leaves your machine
- โ Simple, Intuitive API - Pythonic interface for all operations
- โ Comprehensive Error Handling - Clear exception hierarchy
๐ฆ Installation
pip install neocore-sdk
Requirements:
- Python 3.8 or higher
cryptography>=41.0.0requests>=2.31.0
๐ Quick Start
1. Generate Ed25519 Key Pair
# Using the SDK CLI (recommended)
neocore-keygen
# Or using OpenSSL
openssl genpkey -algorithm ED25519 -out ~/.neocore/p_a1b2c3.pem
openssl pkey -in ~/.neocore/p_a1b2c3.pem -pubout -out ~/.neocore/pub_a1b2c3.pem
c This creates:
~/.neocore/p_a1b2c3.pem- Keep secret! Never share or commit to git~/.neocore/pub_a1b2c3.pem- Safe to share with NeoCore API
2. Register Your Public Key
Register your public key with the NeoCore API (requires Firebase authentication):
import requests
import uuid
# Read your public key
with open("~/.neocore/pub_a1b2c3.pem", "r") as f:
public_key_pem = f.read()
# Get Firebase JWT from your authentication flow
firebase_jwt = "your-firebase-jwt-token"
# Generate unique key ID
key_id = str(uuid.uuid4())
# Register with NeoCore API
response = requests.post(
"https://api.neosapien.xyz/api/v1/dev-keys",
headers={"Authorization": f"Bearer {firebase_jwt}"},
json={
"key_id": key_id,
"public_key_pem": public_key_pem
}
)
# Save this key_id - you'll need it for all SDK requests
print(f"Your key_id: {key_id}")
3. Make Authenticated Requests
from neocore_sdk import NeoCoreClient
# Initialize client (auto-discovers private key from default locations)
# Uses default NeoCore API URL: https://api.neosapien.xyz
client = NeoCoreClient(key_id="your-key-id-from-step-2")
# Make signed requests - that's it!
response = client.get("/api/v1/users")
print(response.json())
๐ Complete Usage Guide
Initialize the Client
from neocore_sdk import NeoCoreClient
# Basic initialization (uses default NeoCore API URL)
client = NeoCoreClient(key_id="your-key-id")
# Custom base URL (for different environments or custom deployments)
client = NeoCoreClient(
key_id="your-key-id",
base_url="https://custom-api.example.com"
)
# With custom private key path
client = NeoCoreClient(
key_id="your-key-id",
private_key_path="/custom/path/to/p_a1b2c3.pem"
)
# With custom timeout
client = NeoCoreClient(
key_id="your-key-id",
timeout=60 # seconds (default: 30)
)
# All options combined
client = NeoCoreClient(
key_id="your-key-id",
base_url="https://staging-api.neosapien.xyz",
private_key_path="/secure/keys/staging_p_a1b2c3.pem",
timeout=45
)
HTTP Methods
All HTTP methods are supported with automatic signing:
GET Request
# Simple GET
response = client.get("/api/v1/users")
# GET with query parameters
response = client.get("/api/v1/users", params={
"page": 1,
"limit": 20,
"sort": "created_at",
"order": "desc"
})
POST Request
# POST with JSON body
response = client.post("/api/v1/users", data={
"name": "John Doe",
"email": "john@example.com",
"role": "developer"
})
# POST with nested data
response = client.post("/api/v1/projects", data={
"name": "AI Assistant",
"metadata": {
"tech_stack": ["Python", "FastAPI"],
"status": "active"
},
"tags": ["ai", "api"]
})
PUT Request (Full Update)
response = client.put("/api/v1/users/123", data={
"name": "Jane Doe",
"email": "jane@example.com",
"role": "admin"
})
PATCH Request (Partial Update)
# Update only specific fields
response = client.patch("/api/v1/users/123", data={
"email": "newemail@example.com"
})
DELETE Request
# Simple DELETE
response = client.delete("/api/v1/users/123")
# DELETE with query parameters
response = client.delete("/api/v1/sessions", params={
"status": "expired",
"older_than_days": 30
})
Response Handling
All methods return a requests.Response object:
response = client.get("/api/v1/users")
# Status code
print(response.status_code) # 200, 401, 404, etc.
# JSON response
data = response.json()
# Raw text
text = response.text
# Headers
print(response.headers)
# Check if successful (2xx status)
if response.ok:
print("Success!")
else:
print(f"Error: {response.status_code}")
๐ Private Key Management
Default Key Locations
The SDK automatically searches for your private key in these locations (in order):
Linux/macOS
~/.neocore/p_a1b2c3.pemโญ RECOMMENDED~/.ssh/p_a1b2c3.pem/etc/neocore/p_a1b2c3.pem./p_a1b2c3.pem(current directory)
Windows
%USERPROFILE%\.neocore\p_a1b2c3.pemโญ RECOMMENDED%APPDATA%\neocore\p_a1b2c3.pem%PROGRAMDATA%\neocore\p_a1b2c3.pem.\p_a1b2c3.pem(current directory)
Custom Key Path
client = NeoCoreClient(
key_id="your-key-id",
private_key_path="/secure/vault/my-key.pem"
)
๐ก๏ธ Exception Handling
The SDK provides a comprehensive exception hierarchy for precise error handling.
Exception Hierarchy
SecureSDKError (Base exception)
โโโ KeyLoadError (Key loading failures)
โ โโโ KeyNotFoundError (No key found in default locations)
โโโ SigningError (Signature generation failures)
โโโ APIConnectionError (HTTP connection failures)
โโโ CanonicalizationError (Request canonicalization failures)
Import Exceptions
from neocore_sdk import (
NeoCoreClient,
SecureSDKError,
KeyLoadError,
KeyNotFoundError,
SigningError,
APIConnectionError,
CanonicalizationError
)
Exception Examples
KeyLoadError
Raised when private key cannot be loaded or is invalid.
try:
client = NeoCoreClient(
key_id="your-key-id",
private_key_path="/invalid/path/p_a1b2c3.pem"
)
except KeyLoadError as e:
print(f"Failed to load private key: {e}")
print("Solution: Check the key path and file permissions")
KeyNotFoundError
Raised when no private key is found in default locations (inherits from KeyLoadError).
try:
client = NeoCoreClient(
key_id="your-key-id"
# No private_key_path specified - will auto-discover
)
except KeyNotFoundError as e:
print(f"No private key found: {e}")
print("Solution: Generate keys with: neocore-keygen")
except KeyLoadError as e:
print(f"Key load error: {e}")
SigningError
Raised when signature generation fails.
try:
response = client.post("/api/v1/users", data={"name": "John"})
except SigningError as e:
print(f"Failed to sign request: {e}")
print("Solution: Verify your private key is valid")
APIConnectionError
Raised when HTTP connection to API fails.
try:
response = client.get("/api/v1/users")
except APIConnectionError as e:
print(f"Connection failed: {e}")
print("Solution: Check network connectivity and base_url")
CanonicalizationError
Raised when request canonicalization fails (rare).
try:
response = client.get("/api/v1/users")
except CanonicalizationError as e:
print(f"Canonicalization failed: {e}")
print("Solution: Contact support - this is unusual")
Comprehensive Error Handling
from neocore_sdk import (
NeoCoreClient,
KeyLoadError,
KeyNotFoundError,
SigningError,
APIConnectionError,
CanonicalizationError
)
try:
# Initialize client
client = NeoCoreClient(key_id="your-key-id")
# Make request
response = client.get("/api/v1/users", params={"page": 1})
# Handle HTTP errors
if response.status_code == 200:
users = response.json()
print(f"Found {len(users)} users")
elif response.status_code == 401:
print("Unauthorized: Check your key_id or signature")
elif response.status_code == 403:
print("Forbidden: Insufficient permissions")
elif response.status_code == 404:
print("Not found: Check endpoint path")
elif response.status_code == 429:
print("Rate limited: Too many requests")
else:
print(f"HTTP Error {response.status_code}: {response.text}")
except KeyNotFoundError as e:
print(f"โ No private key found: {e}")
print("Run: neocore-keygen")
except KeyLoadError as e:
print(f"โ Key load error: {e}")
print("Check file path and permissions")
except SigningError as e:
print(f"โ Signing failed: {e}")
print("Verify your private key is valid")
except APIConnectionError as e:
print(f"โ Connection error: {e}")
print("Check network and base_url")
except CanonicalizationError as e:
print(f"โ Canonicalization error: {e}")
print("Contact support")
except Exception as e:
print(f"โ Unexpected error: {e}")
๐ง CLI Tools
The SDK includes command-line tools for key management.
neocore-keygen
Generate a new Ed25519 key pair.
# Generate in default location (~/.neocore/)
neocore-keygen
# Generate in custom location
neocore-keygen --output-dir /path/to/keys
# Overwrite existing keys without prompting
neocore-keygen --force
# Combine options
neocore-keygen --output-dir /secure/keys --force
Options:
--output-dir DIR- Directory to save keys (default:~/.neocore)--force- Overwrite existing keys without prompting
Output:
โ
Key pair generated successfully!
Private key: p_a1b2c3.pem (Keep this SECRET!)
Public key: pub_a1b2c3.pem (Share with NeoCore API)
neocore-verify-key
Verify an Ed25519 private key and extract public key.
# Verify default key
neocore-verify-key
# Verify custom key
neocore-verify-key /path/to/p_a1b2c3.pem
Output:
โ
Private key is valid!
Algorithm: Ed25519
Key file: /home/user/.neocore/p_a1b2c3.pem
Permissions: 600 (Secure)
๐ Public Key:
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA...
-----END PUBLIC KEY-----
๐ Security Features
How It Works
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Client Side (SDK) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 1. Prepare Request โ
โ - Method: GET, POST, etc. โ
โ - Path: /api/v1/users โ
โ - Query: ?page=1&limit=10 โ
โ - Body: {"name": "John"} โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 2. Generate Security Metadata โ
โ - Timestamp: Unix seconds (e.g., 1704214800) โ
โ - Nonce: UUID v4 (e.g., a1b2c3d4-...) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 3. Build Canonical String โ
โ GET โ
โ /api/v1/users โ
โ limit=10&page=1 โ
โ e3b0c44... (SHA256 of body) โ
โ 1704214800 โ
โ a1b2c3d4-... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 4. Sign with Private Key (Ed25519) โ
โ signature = sign(canonical_string, private_key) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 5. Add Headers to Request โ
โ Content-Type: application/json โ
โ X-Key-Id: your-key-id โ
โ X-Timestamp: 1704214800 โ
โ X-Nonce: a1b2c3d4-... โ
โ X-Signature: abc123... (hex) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 6. Send HTTP Request โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Server Side (NeoCore API) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ 1. Extract X-Key-Id from headers โ
โ 2. Fetch public key from database โ
โ 3. Rebuild canonical string from request โ
โ 4. Verify signature using public key โ
โ 5. Validate timestamp (within time window) โ
โ 6. Check nonce uniqueness (prevent replay) โ
โ 7. โ
Authorized โ Process request โ
โ โ Unauthorized โ Return 401 โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Automatic Security Headers
Every request includes these headers (automatically added by SDK):
| Header | Description | Example |
|---|---|---|
Content-Type |
Request content type | application/json |
X-Key-Id |
Your registered key ID | a1b2c3d4-uuid |
X-Timestamp |
Unix timestamp (seconds) | 1704214800 |
X-Nonce |
Unique request ID (UUID v4) | f1e2d3c4-uuid |
X-Signature |
Ed25519 signature (hex) | 9a8b7c6d... |
Security Benefits
-
Zero-Trust Architecture
- Private key never leaves your machine
- Server never knows your secret
- Database breach doesn't compromise credentials
-
Request Integrity
- Signature covers method, path, query, body, timestamp, nonce
- Any tampering invalidates signature
- Man-in-the-middle attacks prevented
-
Replay Attack Protection
- Timestamp validation (requests expire in 5 minutes)
- Nonce uniqueness check (prevent duplicate requests)
- Old requests automatically rejected
-
Instant Revocation
- Server can revoke key_id instantly
- No token expiration wait time
- Granular access control per key
๐ API Reference
NeoCoreClient
Main client class for making authenticated requests.
Constructor
NeoCoreClient(
key_id: str,
base_url: str = "https://api.neosapien.xyz",
private_key_path: Optional[str] = None,
timeout: int = 30
)
Parameters:
key_id(str): Your registered developer key ID (required)base_url(str): API base URL (default:"https://api.neosapien.xyz")- Override for custom deployments, staging, or development environments
private_key_path(Optional[str]): Path to private key PEM file. IfNone, searches default locations.timeout(int): Request timeout in seconds (default: 30)
Raises:
KeyLoadError: If private key cannot be loadedKeyNotFoundError: If no key found in default locations
Examples:
# Basic (uses default NeoCore API)
client = NeoCoreClient(key_id="a1b2c3d4-5678-90ab-cdef-1234567890ab")
# Custom base URL
client = NeoCoreClient(
key_id="a1b2c3d4-5678-90ab-cdef-1234567890ab",
base_url="https://staging-api.neosapien.xyz"
)
Methods
All methods automatically sign requests and return requests.Response.
get()
get(endpoint: str, params: Optional[dict] = None) -> requests.Response
Send signed GET request.
Parameters:
endpoint(str): API endpoint pathparams(Optional[dict]): Query parameters
Returns: requests.Response
Example:
response = client.get("/api/v1/users", params={"page": 1})
post()
post(endpoint: str, data: Optional[dict] = None) -> requests.Response
Send signed POST request.
Parameters:
endpoint(str): API endpoint pathdata(Optional[dict]): JSON body data
Returns: requests.Response
Example:
response = client.post("/api/v1/users", data={"name": "John"})
put()
put(endpoint: str, data: Optional[dict] = None) -> requests.Response
Send signed PUT request (full update).
Parameters:
endpoint(str): API endpoint pathdata(Optional[dict]): JSON body data
Returns: requests.Response
Example:
response = client.put("/api/v1/users/123", data={"name": "Jane"})
patch()
patch(endpoint: str, data: Optional[dict] = None) -> requests.Response
Send signed PATCH request (partial update).
Parameters:
endpoint(str): API endpoint pathdata(Optional[dict]): JSON body data
Returns: requests.Response
Example:
response = client.patch("/api/v1/users/123", data={"email": "new@email.com"})
delete()
delete(endpoint: str, params: Optional[dict] = None) -> requests.Response
Send signed DELETE request.
Parameters:
endpoint(str): API endpoint pathparams(Optional[dict]): Query parameters
Returns: requests.Response
Example:
response = client.delete("/api/v1/users/123")
๐ฏ Best Practices
1. Key Security
# โ
GOOD: Keep private keys secure
# - Store in ~/.neocore/ with permissions 600
# - Never commit to git (add *.pem to .gitignore)
# - Use environment variables in production
# โ BAD: Don't hardcode key paths in source code
client = NeoCoreClient(
key_id="key-123",
private_key_path="/hardcoded/path/p_a1b2c3.pem" # Bad!
)
# โ
GOOD: Use environment variables
import os
client = NeoCoreClient(
key_id=os.getenv("NEOCORE_KEY_ID"),
base_url=os.getenv("NEOCORE_API_URL", "https://api.neosapien.xyz"),
private_key_path=os.getenv("NEOCORE_PRIVATE_KEY_PATH")
)
2. Error Handling
# โ
GOOD: Catch specific exceptions
try:
response = client.get("/api/v1/users")
except KeyNotFoundError:
# Specific handling for missing key
print("Generate keys with: neocore-keygen")
except APIConnectionError:
# Specific handling for network issues
print("Check your network connection")
# โ BAD: Catch generic exceptions
try:
response = client.get("/api/v1/users")
except Exception as e:
print(f"Something went wrong: {e}")
3. Response Handling
# โ
GOOD: Check status codes properly
response = client.get("/api/v1/users")
if response.status_code == 200:
data = response.json()
# Process data
elif response.status_code == 401:
print("Unauthorized")
elif response.status_code == 404:
print("Not found")
# โ BAD: Assume all responses are successful
response = client.get("/api/v1/users")
data = response.json() # May fail if status is not 200
4. Key Rotation
# Regularly rotate your keys
# 1. Generate new key pair
# 2. Register new public key with new key_id
# 3. Update your application to use new key_id
# 4. After verification, delete old key from server
# 5. Delete old private key from your system
๐ Troubleshooting
"No private key found"
Error:
KeyNotFoundError: No private key found in default locations
Solution:
# Generate keys
neocore-keygen
# Or specify custom path
client = NeoCoreClient(
key_id="...",
private_key_path="/custom/path/p_a1b2c3.pem"
)
"Invalid signature"
Error:
HTTP 401: Invalid signature
Causes & Solutions:
- Wrong key_id: Verify
key_idmatches registered public key - Key mismatch: Private key doesn't match registered public key
- Clock skew: System time is incorrect (timestamp validation fails)
# Sync system time sudo ntpdate -s time.nist.gov # Linux/macOS
"Connection error"
Error:
APIConnectionError: Failed to connect to https://api.example.com
Solutions:
- Check network connectivity
- Verify
base_urlis correct - Check if API server is running
- Verify firewall/proxy settings
"Permission denied" (Key file)
Error:
PermissionError: [Error no. 13] Permission denied: '/path/to/p_a1b2c3.pem'
Solution:
# Fix file permissions
chmod 600 ~/.neocore/p_a1b2c3.pem
๐ Examples
See the examples directory for more:
- basic_usage.py - Simple usage examples
- register_public_key.py - Key registration flow
๐ค Contributing
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
๐ License
This project is licensed under the MIT License - see LICENSE file for details.
๐ Links
- Documentation: https://docs.neosapien.xyz
- GitHub: https://github.com/neosapien/neocore-sdk
- Issues: https://github.com/neosapien/neocore-sdk/issues
- PyPI: https://pypi.org/project/neocore-sdk
๐ก Support
For help and support:
- ๐ง Email: support@neosapien.xyz
- ๐ฌ GitHub Issues: Report an issue
- ๐ Documentation: https://docs.neosapien.xyz
๐ Quick Reference
# Install
pip install neocore-sdk
# Generate keys
neocore-keygen
# Import and use
from neocore_sdk import NeoCoreClient
# Initialize (uses default NeoCore API URL)
client = NeoCoreClient(key_id="your-key-id")
# Make requests
response = client.get("/api/v1/users")
response = client.post("/api/v1/users", data={"name": "John"})
response = client.put("/api/v1/users/123", data={"name": "Jane"})
response = client.patch("/api/v1/users/123", data={"email": "new@email.com"})
response = client.delete("/api/v1/users/123")
# Custom base URL (for staging/development)
staging_client = NeoCoreClient(
key_id="staging-key-id",
base_url="https://staging-api.neosapien.xyz"
)
Made by NeoSapien
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file neocore_sdk-1.0.0.tar.gz.
File metadata
- Download URL: neocore_sdk-1.0.0.tar.gz
- Upload date:
- Size: 44.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a620a1f49a9b9f21e0a0fedbcfb1e4e3522b8882bddb0d966e8b620861d19746
|
|
| MD5 |
7bb8179df955ae1b3428b94cda964993
|
|
| BLAKE2b-256 |
8089758f19289e20ac2dae75932eeeee56415940c9a1184e68f1ab489f9110a6
|
File details
Details for the file neocore_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: neocore_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 31.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7bdfa780a06d56c71db740864f6bfb969df2a0f706f712318f5a273c1c1999a8
|
|
| MD5 |
4a19c79a6cb082869642f41c70a99717
|
|
| BLAKE2b-256 |
f1e1a708336b1ab0875baff756126596fc193430c31996dd796aed2f53728173
|