Metadata-Version: 2.4
Name: nomyo
Version: 0.2.9
Summary: OpenAI-compatible secure chat client with end-to-end encryption for NOMYO Inference Endpoints
Project-URL: Homepage, https://www.nomyo.ai
Project-URL: Documentation, https://bitfreedom.net/code/nomyo-ai/nomyo/wiki/NOMYO-Secure-Client-Documentation
Project-URL: Repository, https://bitfreedom.net/code/nomyo-ai/nomyo
Project-URL: Issues, https://bitfreedom.net/code/nomyo-ai/nomyo/issues
Author-email: "NOMYO.AI" <ichi@nomyo.ai>
License: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Chat
Classifier: Topic :: Security :: Cryptography
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: anyio==4.13.0
Requires-Dist: certifi==2026.4.22
Requires-Dist: cffi==2.0.0
Requires-Dist: cryptography==48.0.0
Requires-Dist: exceptiongroup==1.3.1
Requires-Dist: h11==0.16.0
Requires-Dist: httpcore==1.0.9
Requires-Dist: httpx==0.28.1
Requires-Dist: idna==3.14
Requires-Dist: pycparser==3.0
Requires-Dist: typing-extensions==4.15.0
Description-Content-Type: text/markdown

# NOMYO Secure Python Chat Client

**OpenAI-compatible secure chat client with end-to-end encryption with NOMYO Inference Endpoints**

🔒 **All prompts and responses are automatically encrypted and decrypted**

🔑 **Uses hybrid encryption (AES-256-GCM + RSA-OAEP with 4096-bit keys)**

🔄 **Drop-in replacement for OpenAI's ChatCompletion API**

## 🚀 Quick Start

### 0. Try It Now (Demo Credentials)

No account needed — use these public demo credentials to test immediately:

| | |
|---|---|
| **API key** | `NOMYO_AI_E2EE_INFERENCE` |
| **Model** | `Qwen/Qwen3-0.6B` |

> **Note:** The demo endpoint uses a fixed 256-token context window and is intended for evaluation only.

### 1. Install methods

via pip (recommended):

```bash
pip install nomyo
```

from source:

```bash
git clone https://bitfreedom.net/code/nomyo-ai/nomyo.git
cd nomyo
pip install -r requirements.txt
pip install -e .
```

### 2. Use the client (same API as OpenAI)

```python
import asyncio
from nomyo import SecureChatCompletion

async def main():
    # Initialize client (defaults to https://api.nomyo.ai)
    client = SecureChatCompletion(base_url="https://api.nomyo.ai")

    # Simple chat completion
    response = await client.create(
        model="Qwen/Qwen3-0.6B",
        messages=[
            {"role": "user", "content": "Hello! How are you today?"}
        ],
        security_tier="standard", #optional: standard, high or maximum
        temperature=0.7
    )

    print(response['choices'][0]['message']['content'])

# Run the async function
asyncio.run(main())
```

## 🔐 Security Features

### Hybrid Encryption

- **Payload encryption**: AES-256-GCM (authenticated encryption)
- **Key exchange**: RSA-OAEP with SHA-256
- **Key size**: 4096-bit RSA keys
- **All communication**: End-to-end encrypted

### Key Management

- **Automatic key generation**: Keys are automatically generated on first use
- **Automatic key loading**: Existing keys are loaded automatically from `client_keys/` directory
- **No manual intervention required**: The library handles key management automatically
- **Keys kept in memory**: Active session keys are stored in memory for performance
- **Optional persistence**: Keys can be saved to `client_keys/` directory for reuse across sessions
- **Password protection**: Optional password encryption for private keys (recommended for production)
- **Secure permissions**: Private keys stored with restricted permissions (600 - owner-only access)

### Secure Memory Protection

### Ephemeral AES Keys

- **Per-request encryption keys**: A unique AES-256 key is generated for each request
- **Automatic rotation**: AES keys are never reused - a fresh key is created for every encryption operation
- **Forward secrecy**: Compromise of one AES key only affects that single request
- **Secure generation**: AES keys are generated using cryptographically secure random number generation (`secrets.token_bytes`)
- **Automatic cleanup**: AES keys are zeroed from memory immediately after use
- **Automatic protection**: Plaintext payloads are automatically protected during encryption
- **Prevents memory swapping**: Sensitive data cannot be swapped to disk
- **Guaranteed zeroing**: Memory is zeroed after encryption completes
- **Fallback mechanism**: Graceful degradation if SecureMemory module unavailable

## 🔄 OpenAI Compatibility

The `SecureChatCompletion` class provides **exact API compatibility** with OpenAI's `ChatCompletion.create()` method.

### Supported Parameters

All standard OpenAI parameters are supported:

- `model`: Model identifier
- `messages`: List of message objects
- `temperature`: Sampling temperature (0-2)
- `max_tokens`: Maximum tokens to generate
- `top_p`: Nucleus sampling
- `frequency_penalty`: Frequency penalty
- `presence_penalty`: Presence penalty
- `stop`: Stop sequences
- `n`: Number of completions
- `stream`: Streaming (not yet implemented)
- `tools`: Tool definitions
- `tool_choice`: Tool selection strategy
- `user`: User identifier
- And more...

### Response Format

Responses follow the OpenAI format exactly, with an additional `_metadata` field for debugging and security information:

```python
{
    "id": "chatcmpl-123",
    "object": "chat.completion",
    "created": 1234567890,
    "model": "Qwen/Qwen3-0.6B",
    "choices": [
        {
            "index": 0,
            "message": {
                "role": "assistant",
                "content": "Hello! I'm doing well, thank you for asking.",
                "tool_calls": [...]  # if tools were used
            },
            "finish_reason": "stop"
        }
    ],
    "usage": {
        "prompt_tokens": 10,
        "completion_tokens": 20,
        "total_tokens": 30
    },
    "_metadata": {
        "payload_id": "openai-compat-abc123",  # Unique identifier for this request
        "processed_at": 1765250382,  # Timestamp when server processed the request
        "is_encrypted": True,  # Indicates this response was decrypted
        "encryption_algorithm": "hybrid-aes256-rsa4096",  # Encryption method used
        "response_status": "success"  # Status of the decryption/processing
    }
}
```

The `_metadata` field contains security-related information about the encrypted communication and is automatically added to all responses.

## 🛠️ Usage Examples

### Basic Chat

```python
import asyncio
from nomyo import SecureChatCompletion

async def main():
    client = SecureChatCompletion(base_url="https://api.nomyo.ai")

    response = await client.create(
        model="Qwen/Qwen3-0.6B",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "What is the capital of France?"}
        ],
        security_tier="standard", #optional: standard, high or maximum
        temperature=0.7
    )

    print(response['choices'][0]['message']['content'])

asyncio.run(main())
```

### With Tools

```python

import asyncio
from nomyo import SecureChatCompletion

async def main():
    client = SecureChatCompletion(base_url="https://api.nomyo.ai")

    response = await client.create(
        model="Qwen/Qwen3-0.6B",
        messages=[
            {"role": "user", "content": "What's the weather in Paris?"}
        ],
        tools=[
            {
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "description": "Get weather information",
                    "parameters": {
                        "type": "object",
                        "properties": {
                            "location": {"type": "string"}
                        },
                        "required": ["location"]
                    }
                }
            }
        ],
        security_tier="standard", #optional: standard, high or maximum
        temperature=0.7
    )

    print(response['choices'][0]['message']['content'])

asyncio.run(main())
```

### Using acreate() Alias

```python
import asyncio
from nomyo import SecureChatCompletion

async def main():
    client = SecureChatCompletion(base_url="https://api.nomyo.ai")

    response = await client.acreate(
        model="Qwen/Qwen3-0.6B",
        messages=[
            {"role": "user", "content": "Hello!"}
        ],
        temperature=0.7
    )

    print(response['choices'][0]['message']['content'])

asyncio.run(main())
```

## 📦 Dependencies

- `anyio`: Async compatibility layer
- `certifi`: TLS/SSL certificates
- `cffi`: C Foreign Function Interface
- `cryptography`: Cryptographic primitives (RSA, AES, etc.)
- `exceptiongroup`: Exception groups backport
- `h11`: HTTP/1.1 protocol implementation
- `httpcore`: Minimal HTTP client
- `httpx`: Async HTTP client
- `idna`: Internationalized domain names
- `pycparser`: C parser for cffi
- `typing_extensions`: Backported typing hints

## 🔧 Configuration

### Custom Base URL

```python
import asyncio
from nomyo import SecureChatCompletion

async def main():
    client = SecureChatCompletion(base_url="https://NOMYO-Pro-Router:12434")
    # ... rest of your code
    asyncio.run(main())
```### API Key Authentication

```python
import asyncio
from nomyo import SecureChatCompletion

async def main():
    # Initialize with API key (recommended for production)
    client = SecureChatCompletion(
        base_url="https://api.nomyo.ai",
        api_key="your-api-key-here"
    )

    # Or pass API key in the create() method
    response = await client.create(
        model="Qwen/Qwen3-0.6B",
        messages=[
            {"role": "user", "content": "Hello!"}
        ],
        api_key="your-api-key-here"  # Overrides instance API key
    )

asyncio.run(main())
```

### Secure Memory Configuration

```python
import asyncio
from nomyo import SecureChatCompletion

async def main():
    # Enable secure memory protection (default, recommended)
    client = SecureChatCompletion(
        base_url="https://api.nomyo.ai",
        secure_memory=True  # Default
    )

    # Disable secure memory (not recommended, for testing only)
    client = SecureChatCompletion(
        base_url="https://api.nomyo.ai",
        secure_memory=False
    )

asyncio.run(main())
```

### Key Management

Keys are automatically generated on first use.

#### Generate Keys Manually

```python
import asyncio
from nomyo.SecureCompletionClient import SecureCompletionClient

async def main():
    client = SecureCompletionClient()
    await client.generate_keys(save_to_file=True, password="your-password")

asyncio.run(main())
```

#### Load Existing Keys

```python
import asyncio
from nomyo.SecureCompletionClient import SecureCompletionClient

async def main():
    client = SecureCompletionClient()
    await client.load_keys("client_keys/private_key.pem", "client_keys/public_key.pem", password="your-password")

asyncio.run(main())
```

## 📚 API Reference

### SecureChatCompletion

#### Constructor

```python
SecureChatCompletion(
    base_url: str = "https://api.nomyo.ai",
    allow_http: bool = False,
    api_key: Optional[str] = None,
    secure_memory: bool = True,
    max_retries: int = 2
)
```

**Parameters:**

- `base_url`: Base URL of the NOMYO Router (must use HTTPS for production)
- `allow_http`: Allow HTTP connections (ONLY for local development, never in production)
- `api_key`: Optional API key for bearer authentication
- `secure_memory`: Enable secure memory protection (default: True)
- `max_retries`: Retries on retryable errors (429, 500, 502, 503, 504, network errors) with exponential backoff. Default: 2

#### Methods

- `create(model, messages, **kwargs)`: Create a chat completion
- `acreate(model, messages, **kwargs)`: Async alias for create()

### SecureCompletionClient

#### Constructor

```python
SecureCompletionClient(router_url: str = "https://api.nomyo.ai", allow_http: bool = False, max_retries: int = 2)
```

#### Methods

- `generate_keys(save_to_file=False, key_dir="client_keys", password=None)`: Generate RSA key pair
- `load_keys(private_key_path, public_key_path=None, password=None)`: Load keys from files
- `fetch_server_public_key()`: Fetch server's public key
- `encrypt_payload(payload)`: Encrypt a payload
- `decrypt_response(encrypted_response, payload_id)`: Decrypt a response
- `send_secure_request(payload, payload_id)`: Send encrypted request and receive decrypted response

## 📝 Notes

### Security Best Practices

- Always use password protection for private keys in production
- Keep private keys secure (permissions set to 600)
- Never share your private key
- Verify server's public key fingerprint before first use

### Performance

- Key generation takes ~1-2 seconds (one-time operation)
- Encryption/decryption adds minimal overhead (~10-20ms per request)

### Compatibility

- Works with any OpenAI-compatible code
- No changes needed to existing OpenAI client code
- Simply replace `openai.ChatCompletion.create()` with `SecureChatCompletion.create()`

## 🤝 Contributing

Contributions are welcome! Please open issues or pull requests on the project repository.

## 📄 License

See LICENSE file for licensing information.

## 📞 Support

For questions or issues, please refer to the project documentation or open an issue.
