diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..24b1f8f
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+# Contributing
+
+## Development Setup
+
+```bash
+npm install
+npm run build
+npm test
+```
+
+Node.js 18 LTS or later is required for tests and the TypeScript compiler.
diff --git a/README.md b/README.md
index 2749bf8..e48516f 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,13 @@
-# NOMYO.js - Secure JavaScript Chat Client
+# NOMYO.js โ Secure JavaScript Chat Client
**OpenAI-compatible secure chat client with end-to-end encryption for 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**
-๐ **Works in both Node.js and browsers**
+- All prompts and responses are automatically encrypted and decrypted
+- Hybrid encryption: AES-256-GCM payload + RSA-OAEP-SHA256 key exchange, 4096-bit keys
+- Drop-in replacement for OpenAI's ChatCompletion API
+- Works in both Node.js and browsers
-## ๐ Quick Start
+## Quick Start
### Installation
@@ -20,371 +20,323 @@ npm install nomyo-js
```javascript
import { SecureChatCompletion } from 'nomyo-js';
-// Initialize client (defaults to https://api.nomyo.ai:12434)
const client = new SecureChatCompletion({
- baseUrl: 'https://api.nomyo.ai:12434'
+ apiKey: process.env.NOMYO_API_KEY,
});
-// Simple chat completion
const response = await client.create({
model: 'Qwen/Qwen3-0.6B',
- messages: [
- { role: 'user', content: 'Hello! How are you today?' }
- ],
- temperature: 0.7
+ messages: [{ role: 'user', content: 'Hello!' }],
+ temperature: 0.7,
});
console.log(response.choices[0].message.content);
+client.dispose();
```
### Basic Usage (Browser)
```html
-
-
-
-
-
-
- NOMYO Secure Chat
-
-
+ console.log(response.choices[0].message.content);
+
```
-## ๐ Security Features
+## Documentation
+
+Full documentation is in the [`doc/`](doc/) directory:
+
+- [Getting Started](doc/getting-started.md) โ walkthrough for new users
+- [API Reference](doc/api-reference.md) โ complete constructor options, methods, types, and error classes
+- [Models](doc/models.md) โ available models and selection guide
+- [Security Guide](doc/security-guide.md) โ encryption, memory protection, key management, compliance
+- [Rate Limits](doc/rate-limits.md) โ limits, automatic retry behaviour, batch throttling
+- [Examples](doc/examples.md) โ 12+ code examples for common scenarios
+- [Troubleshooting](doc/troubleshooting.md) โ error reference and debugging tips
+
+## Security Features
### Hybrid Encryption
--**Payload encryption**: AES-256-GCM (authenticated encryption)
-- **Key exchange**: RSA-OAEP with SHA-256
+- **Payload encryption**: AES-256-GCM (authenticated encryption)
+- **Key exchange**: RSA-OAEP-SHA256
- **Key size**: 4096-bit RSA keys
-- **All communication**: End-to-end encrypted
+- **Scope**: All communication is 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 (Node.js only)
-- **No manual intervention required**: The library handles key management automatically
-- **Optional persistence**: Keys can be saved to `client_keys/` directory for reuse across sessions (Node.js only)
-- **Password protection**: Optional password encryption for private keys (minimum 8 characters required)
-- **Secure permissions**: Private keys stored with restricted permissions (600 - owner-only access)
-- **Automatic key rotation**: Keys are rotated on a configurable interval (default: 24 hours) to limit fingerprint lifetime
-- **Explicit lifecycle management**: Call `dispose()` to immediately zero in-memory key material and stop the rotation timer
+- **Automatic**: Keys are generated on first use and saved to `keyDir` (default: `client_keys/`). Existing keys are reloaded on subsequent runs. Node.js only.
+- **Password protection**: Optional AES-encrypted private key files (minimum 8 characters).
+- **Secure permissions**: Private key files saved at `0600` (owner-only).
+- **Auto-rotation**: Keys rotate every 24 hours by default (configurable via `keyRotationInterval`).
+- **Explicit lifecycle**: Call `dispose()` to zero in-memory key material and stop the rotation timer.
-### Secure Memory Protection
+### Secure Memory
-> [!NOTE]
-> **Pure JavaScript Implementation**: This version uses pure JavaScript with immediate memory zeroing.
-> OS-level memory locking (`mlock`) is NOT available without a native addon.
-> For enhanced security in production, consider implementing the optional native addon (see `native/` directory).
+The library wraps all intermediate sensitive buffers (AES keys, plaintext payload, decrypted bytes) in `SecureByteContext`, which zeroes them in a `finally` block immediately after use.
-- **Automatic cleanup**: Sensitive data is zeroed from memory immediately after use
-- **Best-effort protection**: Minimizes exposure time of sensitive data
-- **Fallback mechanism**: Graceful degradation if enhanced security is 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
-- `tools`: Tool definitions
-- `tool_choice`: Tool selection strategy
-- `user`: User identifier
-
-### Response Format
-
-Responses follow the OpenAI format exactly, with an additional `_metadata` field for debugging and security information:
+Pure JavaScript cannot lock pages to prevent OS swapping (`mlock`). For environments where swap-file exposure is unacceptable, install the optional `nomyo-native` addon. Check the current protection level:
```javascript
-{
- "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."
- },
- "finish_reason": "stop"
- }
- ],
- "usage": {
- "prompt_tokens": 10,
- "completion_tokens": 20,
- "total_tokens": 30
- },
- "_metadata": {
- "payload_id": "openai-compat-abc123",
- "processed_at": 1765250382,
- "is_encrypted": true,
- "encryption_algorithm": "hybrid-aes256-rsa4096",
- "response_status": "success"
- }
+import { getMemoryProtectionInfo } from 'nomyo-js';
+
+const info = getMemoryProtectionInfo();
+// Without addon: { method: 'zero-only', canLock: false }
+// With addon: { method: 'mlock', canLock: true }
+```
+
+### Security Tiers
+
+Pass `security_tier` per request to route inference to increasingly isolated hardware:
+
+| Tier | Hardware | Use case |
+|------|----------|----------|
+| `"standard"` | GPU | General secure inference |
+| `"high"` | CPU/GPU balanced | Sensitive business data |
+| `"maximum"` | CPU only | HIPAA PHI, classified data |
+
+```javascript
+const response = await client.create({
+ model: 'Qwen/Qwen3-0.6B',
+ messages: [{ role: 'user', content: 'Patient record summary...' }],
+ security_tier: 'maximum',
+});
+```
+
+## Usage Examples
+
+### With API Key
+
+```javascript
+const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });
+```
+
+### Error Handling
+
+```javascript
+import {
+ SecureChatCompletion,
+ AuthenticationError,
+ RateLimitError,
+ ForbiddenError,
+} from 'nomyo-js';
+
+try {
+ const response = await client.create({ model: 'Qwen/Qwen3-0.6B', messages: [...] });
+} catch (err) {
+ if (err instanceof AuthenticationError) console.error('Check API key:', err.message);
+ else if (err instanceof RateLimitError) console.error('Rate limit hit:', err.message);
+ else if (err instanceof ForbiddenError) console.error('Model/tier mismatch:', err.message);
+ else throw err;
}
```
-## ๐ ๏ธ Usage Examples
+### Per-Request Router Override
-### Basic Chat
+Send a single request to a different router without changing the main client:
```javascript
-import { SecureChatCompletion } from 'nomyo-js';
-
-const client = new SecureChatCompletion({
- baseUrl: 'https://api.nomyo.ai:12434'
-});
-
const 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?' }
- ],
- temperature: 0.7
+ model: 'Qwen/Qwen3-0.6B',
+ messages: [{ role: 'user', content: 'Hello from secondary router' }],
+ base_url: 'https://secondary.nomyo.ai:12435', // temporary โ main client unchanged
});
-
-console.log(response.choices[0].message.content);
```
-### With Tools
+### Tool / Function Calling
```javascript
const response = await client.create({
- model: 'Qwen/Qwen3-0.6B',
- messages: [
- { role: 'user', content: "What's the weather in Paris?" }
- ],
+ 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',
+ name: 'get_weather',
+ description: 'Get weather information for a location',
parameters: {
type: 'object',
- properties: {
- location: { type: 'string' }
- },
- required: ['location']
- }
- }
- }
- ]
+ properties: { location: { type: 'string' } },
+ required: ['location'],
+ },
+ },
+ },
+ ],
+ tool_choice: 'auto',
});
```
-### With API Key Authentication
+### Thinking Models
```javascript
-const client = new SecureChatCompletion({
- baseUrl: 'https://api.nomyo.ai:12434',
- apiKey: 'your-api-key-here'
-});
-
-// API key will be automatically included in all requests
const response = await client.create({
- model: 'Qwen/Qwen3-0.6B',
- messages: [
- { role: 'user', content: 'Hello!' }
- ]
-});
-```
-
-### Custom Key Management (Node.js)
-
-```javascript
-import { SecureCompletionClient } from 'nomyo-js';
-
-const client = new SecureCompletionClient({
- routerUrl: 'https://api.nomyo.ai:12434'
+ model: 'LiquidAI/LFM2.5-1.2B-Thinking',
+ messages: [{ role: 'user', content: 'Is 9.9 larger than 9.11?' }],
});
-// Generate keys with password protection
-await client.generateKeys({
- saveToFile: true,
- keyDir: 'client_keys',
- password: 'your-secure-password'
-});
-
-// Or load existing keys
-await client.loadKeys(
- 'client_keys/private_key.pem',
- 'client_keys/public_key.pem',
- 'your-secure-password'
-);
+const { content, reasoning_content } = response.choices[0].message;
+console.log('Reasoning:', reasoning_content);
+console.log('Answer:', content);
```
### Resource Management
-Always call `dispose()` when finished to zero key material and stop the background rotation timer:
-
```javascript
-const client = new SecureChatCompletion({
- baseUrl: 'https://api.nomyo.ai:12434',
- keyRotationInterval: 3600000, // rotate every hour
-});
+const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });
try {
const response = await client.create({ model: 'Qwen/Qwen3-0.6B', messages: [...] });
console.log(response.choices[0].message.content);
} finally {
- client.dispose();
+ client.dispose(); // zeros key material, stops rotation timer
}
```
-To disable key rotation entirely (e.g. short-lived scripts):
-
-```javascript
-const client = new SecureChatCompletion({
- baseUrl: 'https://api.nomyo.ai:12434',
- keyRotationInterval: 0, // disabled
-});
-```
-
-## ๐งช Platform Support
-
-### Node.js
-
-- **Minimum version**: Node.js 14.17+
-- **Recommended**: Node.js 18 LTS or later
-- **Key storage**: File system (`client_keys/` directory)
-- **Security**: Full implementation with automatic key persistence
-
-### Browsers
-
-- **Supported browsers**: Modern browsers with Web Crypto API support
- - Chrome 37+
- - Firefox 34+
- - Safari 11+
- - Edge 79+
-- **Key storage**: In-memory only (keys not persisted for security)
-- **Security**: Best-effort memory protection (no OS-level locking)
-
-## ๐ API Reference
-
-### SecureChatCompletion
-
-#### Constructor
-
-```typescript
-new SecureChatCompletion(config?: {
- baseUrl?: string; // Default: 'https://api.nomyo.ai:12434'
- allowHttp?: boolean; // Default: false
- apiKey?: string; // Default: undefined
- secureMemory?: boolean; // Default: true
- timeout?: number; // Request timeout ms. Default: 60000
- debug?: boolean; // Enable verbose logging. Default: false
- keyRotationInterval?: number; // Key rotation ms. 0 = disabled. Default: 86400000 (24h)
- keyRotationDir?: string; // Directory for rotated keys. Default: 'client_keys'
- keyRotationPassword?: string; // Password for rotated key files
-})
-```
-
-#### Methods
-
-- `create(request: ChatCompletionRequest): Promise`
-- `acreate(request: ChatCompletionRequest): Promise` (alias)
-- `dispose(): void` โ zero key material and stop rotation timer
-
-### SecureCompletionClient
-
-Lower-level API for advanced use cases.
-
-#### Constructor
-
-```typescript
-new SecureCompletionClient(config?: {
- routerUrl?: string; // Default: 'https://api.nomyo.ai:12434'
- allowHttp?: boolean; // Default: false
- secureMemory?: boolean; // Default: true
- keySize?: 2048 | 4096; // Default: 4096
- timeout?: number; // Request timeout ms. Default: 60000
- debug?: boolean; // Enable verbose logging. Default: false
- keyRotationInterval?: number; // Key rotation ms. 0 = disabled. Default: 86400000 (24h)
- keyRotationDir?: string; // Directory for rotated keys. Default: 'client_keys'
- keyRotationPassword?: string; // Password for rotated key files
-})
-```
-
-#### Methods
-
-- `generateKeys(options?: KeyGenOptions): Promise`
-- `loadKeys(privateKeyPath: string, publicKeyPath?: string, password?: string): Promise`
-- `fetchServerPublicKey(): Promise`
-- `encryptPayload(payload: object): Promise`
-- `decryptResponse(encrypted: ArrayBuffer, payloadId: string): Promise