nomyo-js/README.md

343 lines
12 KiB
Markdown
Raw Normal View History

2026-04-16 16:44:26 +02:00
# NOMYO.js — Secure JavaScript Chat Client
2026-01-17 12:02:08 +01:00
**OpenAI-compatible secure chat client with end-to-end encryption for NOMYO Inference Endpoints**
2026-04-16 16:44:26 +02:00
- 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
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
## Quick Start
2026-01-17 12:02:08 +01:00
### Installation
```bash
npm install nomyo-js
```
### Basic Usage (Node.js)
```javascript
import { SecureChatCompletion } from 'nomyo-js';
const client = new SecureChatCompletion({
2026-04-16 16:44:26 +02:00
apiKey: process.env.NOMYO_API_KEY,
2026-01-17 12:02:08 +01:00
});
const response = await client.create({
model: 'Qwen/Qwen3-0.6B',
2026-04-16 16:44:26 +02:00
messages: [{ role: 'user', content: 'Hello!' }],
temperature: 0.7,
2026-01-17 12:02:08 +01:00
});
console.log(response.choices[0].message.content);
2026-04-16 16:44:26 +02:00
client.dispose();
2026-01-17 12:02:08 +01:00
```
### Basic Usage (Browser)
```html
2026-04-16 16:44:26 +02:00
<script type="module">
import { SecureChatCompletion } from 'https://unpkg.com/nomyo-js/dist/browser/index.js';
const client = new SecureChatCompletion({
baseUrl: 'https://api.nomyo.ai',
apiKey: 'your-api-key',
});
const response = await client.create({
model: 'Qwen/Qwen3-0.6B',
messages: [{ role: 'user', content: 'What is 2+2?' }],
});
console.log(response.choices[0].message.content);
</script>
2026-01-17 12:02:08 +01:00
```
2026-04-16 16:44:26 +02:00
## 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
2026-01-17 12:02:08 +01:00
### Hybrid Encryption
2026-04-16 16:44:26 +02:00
- **Payload encryption**: AES-256-GCM (authenticated encryption)
- **Key exchange**: RSA-OAEP-SHA256
2026-01-17 12:02:08 +01:00
- **Key size**: 4096-bit RSA keys
2026-04-16 16:44:26 +02:00
- **Scope**: All communication is end-to-end encrypted
2026-01-17 12:02:08 +01:00
### Key Management
2026-04-16 16:44:26 +02:00
- **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.
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### Secure Memory
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
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.
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
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:
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
```javascript
import { getMemoryProtectionInfo } from 'nomyo-js';
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
const info = getMemoryProtectionInfo();
// Without addon: { method: 'zero-only', canLock: false }
// With addon: { method: 'mlock', canLock: true }
```
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### Security Tiers
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
Pass `security_tier` per request to route inference to increasingly isolated hardware:
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
| Tier | Hardware | Use case |
|------|----------|----------|
| `"standard"` | GPU | General secure inference |
| `"high"` | CPU/GPU balanced | Sensitive business data |
| `"maximum"` | CPU only | HIPAA PHI, classified data |
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
```javascript
const response = await client.create({
model: 'Qwen/Qwen3-0.6B',
messages: [{ role: 'user', content: 'Patient record summary...' }],
security_tier: 'maximum',
});
```
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
## Usage Examples
### With API Key
2026-01-17 12:02:08 +01:00
```javascript
2026-04-16 16:44:26 +02:00
const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });
2026-01-17 12:02:08 +01:00
```
2026-04-16 16:44:26 +02:00
### Error Handling
2026-01-17 12:02:08 +01:00
```javascript
2026-04-16 16:44:26 +02:00
import {
SecureChatCompletion,
AuthenticationError,
RateLimitError,
ForbiddenError,
} from 'nomyo-js';
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
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;
}
```
### Per-Request Router Override
Send a single request to a different router without changing the main client:
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
```javascript
2026-01-17 12:02:08 +01:00
const response = await client.create({
2026-04-16 16:44:26 +02:00
model: 'Qwen/Qwen3-0.6B',
messages: [{ role: 'user', content: 'Hello from secondary router' }],
base_url: 'https://secondary.nomyo.ai:12435', // temporary — main client unchanged
2026-01-17 12:02:08 +01:00
});
```
2026-04-16 16:44:26 +02:00
### Tool / Function Calling
2026-01-17 12:02:08 +01:00
```javascript
const response = await client.create({
2026-04-16 16:44:26 +02:00
model: 'Qwen/Qwen3-0.6B',
messages: [{ role: 'user', content: "What's the weather in Paris?" }],
2026-01-17 12:02:08 +01:00
tools: [
{
type: 'function',
function: {
2026-04-16 16:44:26 +02:00
name: 'get_weather',
description: 'Get weather information for a location',
2026-01-17 12:02:08 +01:00
parameters: {
type: 'object',
2026-04-16 16:44:26 +02:00
properties: { location: { type: 'string' } },
required: ['location'],
},
},
},
],
tool_choice: 'auto',
2026-01-17 12:02:08 +01:00
});
```
2026-04-16 16:44:26 +02:00
### Thinking Models
2026-01-17 12:02:08 +01:00
```javascript
const response = await client.create({
2026-04-16 16:44:26 +02:00
model: 'LiquidAI/LFM2.5-1.2B-Thinking',
messages: [{ role: 'user', content: 'Is 9.9 larger than 9.11?' }],
2026-01-17 12:02:08 +01:00
});
2026-04-16 16:44:26 +02:00
const { content, reasoning_content } = response.choices[0].message;
console.log('Reasoning:', reasoning_content);
console.log('Answer:', content);
2026-01-17 12:02:08 +01:00
```
2026-04-01 15:15:18 +02:00
### Resource Management
```javascript
2026-04-16 16:44:26 +02:00
const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });
2026-04-01 15:15:18 +02:00
try {
const response = await client.create({ model: 'Qwen/Qwen3-0.6B', messages: [...] });
console.log(response.choices[0].message.content);
} finally {
2026-04-16 16:44:26 +02:00
client.dispose(); // zeros key material, stops rotation timer
2026-04-01 15:15:18 +02:00
}
```
2026-04-16 16:44:26 +02:00
### Local Development (HTTP)
2026-04-01 15:15:18 +02:00
```javascript
const client = new SecureChatCompletion({
2026-04-16 16:44:26 +02:00
baseUrl: 'http://localhost:12435',
allowHttp: true, // required — also prints a visible warning
2026-04-01 15:15:18 +02:00
});
```
2026-04-16 16:44:26 +02:00
## API Reference
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### `SecureChatCompletion` — Constructor Options
2026-01-17 12:02:08 +01:00
```typescript
2026-04-16 16:44:26 +02:00
new SecureChatCompletion(config?: ChatCompletionConfig)
2026-01-17 12:02:08 +01:00
```
2026-04-16 16:44:26 +02:00
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `baseUrl` | `string` | `'https://api.nomyo.ai'` | NOMYO router URL. Must be HTTPS in production. |
| `allowHttp` | `boolean` | `false` | Allow HTTP connections. Local development only. |
| `apiKey` | `string` | `undefined` | Bearer token for `Authorization` header. |
| `secureMemory` | `boolean` | `true` | Zero sensitive buffers immediately after use. |
| `timeout` | `number` | `60000` | Request timeout in milliseconds. |
| `debug` | `boolean` | `false` | Print verbose logging to the console. |
| `keyDir` | `string` | `'client_keys'` | Directory to load/save RSA keys on startup. |
| `keyRotationInterval` | `number` | `86400000` | Auto-rotate keys every N ms. `0` disables rotation. |
| `keyRotationDir` | `string` | `'client_keys'` | Directory for rotated key files. Node.js only. |
| `keyRotationPassword` | `string` | `undefined` | Password for encrypted rotated key files. |
| `maxRetries` | `number` | `2` | Extra retry attempts on 429/5xx/network errors. Exponential backoff (1 s, 2 s, …). |
2026-01-17 12:02:08 +01:00
#### Methods
2026-04-16 16:44:26 +02:00
- `create(request): Promise<ChatCompletionResponse>` — send an encrypted chat completion
- `acreate(request): Promise<ChatCompletionResponse>` — alias for `create()`
- `dispose(): void` — zero key material and stop the rotation timer
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
#### `create()` Request Fields
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
All standard OpenAI fields (`model`, `messages`, `temperature`, `top_p`, `max_tokens`, `stop`, `n`, `tools`, `tool_choice`, `user`, `frequency_penalty`, `presence_penalty`, `logit_bias`) plus:
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
| Field | Description |
|-------|-------------|
| `security_tier` | `"standard"` \| `"high"` \| `"maximum"` — hardware isolation level |
| `api_key` | Per-request API key override |
| `base_url` | Per-request router URL override — creates a temporary client, used once, then disposed |
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### `SecureCompletionClient` — Constructor Options
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
Lower-level client. All options above apply, with these differences:
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `routerUrl` | `string` | `'https://api.nomyo.ai'` | Base URL (`baseUrl` is renamed here) |
| `keySize` | `2048 \| 4096` | `4096` | RSA modulus length |
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
#### Methods
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
- `generateKeys(options?)` — generate a new RSA key pair
- `loadKeys(privateKeyPath, publicKeyPath?, password?)` — load existing PEM files
- `fetchServerPublicKey()` — fetch the server's RSA public key
- `encryptPayload(payload)` — encrypt a request payload
- `decryptResponse(encrypted, payloadId)` — decrypt a response body
- `sendSecureRequest(payload, payloadId, apiKey?, securityTier?)` — full encrypt → POST → decrypt cycle
- `dispose()` — zero key material and stop rotation timer
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### Secure Memory Public API
```typescript
import {
getMemoryProtectionInfo,
disableSecureMemory,
enableSecureMemory,
SecureByteContext,
} from 'nomyo-js';
2026-01-17 12:02:08 +01:00
```
2026-04-16 16:44:26 +02:00
| Export | Description |
|--------|-------------|
| `getMemoryProtectionInfo()` | Returns `{ method, canLock, isPlatformSecure, details? }` |
| `disableSecureMemory()` | Disable global secure-memory zeroing |
| `enableSecureMemory()` | Re-enable global secure-memory zeroing |
| `SecureByteContext` | Low-level buffer wrapper — zeros in `finally` block |
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### Error Classes
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
```typescript
import {
AuthenticationError, InvalidRequestError, RateLimitError,
ForbiddenError, ServerError, ServiceUnavailableError,
APIConnectionError, SecurityError, DisposedError, APIError,
} from 'nomyo-js';
2026-01-17 12:02:08 +01:00
```
2026-04-16 16:44:26 +02:00
| Class | HTTP | Thrown when |
|-------|------|-------------|
| `AuthenticationError` | 401 | Invalid or missing API key |
| `InvalidRequestError` | 400 | Malformed request |
| `ForbiddenError` | 403 | Model not allowed for the security tier |
| `RateLimitError` | 429 | Rate limit exceeded (after all retries) |
| `ServerError` | 500 | Internal server error (after all retries) |
| `ServiceUnavailableError` | 503 | Backend unavailable (after all retries) |
| `APIError` | varies | Other HTTP errors |
| `APIConnectionError` | — | Network failure or timeout (after all retries) |
| `SecurityError` | — | HTTPS not used, header injection, or crypto failure |
| `DisposedError` | — | Method called after `dispose()` |
## Platform Support
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
### Node.js
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
- **Minimum**: Node.js 14.17+
- **Recommended**: Node.js 18 LTS or later
- **Key storage**: File system (`keyDir` directory, default `client_keys/`)
### Browsers
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
- **Supported**: Chrome 37+, Firefox 34+, Safari 11+, Edge 79+
- **Key storage**: In-memory only (not persisted)
- **Limitation**: File-based key operations (`keyDir`, `loadKeys`) are not available
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
## Security Best Practices
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
- Always use HTTPS (`allowHttp` is `false` by default)
- Load API key from an environment variable, never hardcode it
- Use password-protected key files (`keyRotationPassword`)
- Store keys outside the project directory and outside version control
- Add `client_keys/` and `*.pem` to `.gitignore`
- Call `dispose()` when the client is no longer needed
- Use `security_tier: 'maximum'` for HIPAA PHI or classified data
- Consider the `nomyo-native` addon if swap-file exposure is unacceptable
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
## License
2026-01-17 12:02:08 +01:00
2026-04-16 16:44:26 +02:00
See LICENSE file.