Some checks failed
NYX Security Scan / nyx-scan (pull_request) Failing after 5m51s
BREAKING CHANGE: keyDir now defaults to null (ephemeral) instead of
'client_keys'. Clients that relied on keys persisting across restarts
must now pass keyDir explicitly.
The Python SDK defaults to key_dir=None: a key pair is generated in
memory for the session and never written to disk. The JS port defaulted
to 'client_keys' and always persisted, so merely constructing a client
wrote an RSA private key into the working directory. That is a weaker
default than the client it ports, and one users never asked for.
- keyDir?: string | null, defaulting to undefined. null and undefined
both mean ephemeral, matching Python's None.
- Persistent mode is unchanged when keyDir is set: load the existing
pair from that directory, otherwise generate and save one there.
- Browsers are always ephemeral; they have no filesystem.
Key rotation follows the same rule. It previously hardcoded
'client_keys' as its fallback directory, so an ephemeral client would
have started writing private keys to disk on the first rotation tick.
Rotated keys are now persisted only where keyDir or keyRotationDir is
explicitly configured.
Tests assert the intent (that saveKeys is never called) rather than
probing the filesystem, since a leftover client_keys/ from the old
default would otherwise make them pass or fail for the wrong reason.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
272 lines
14 KiB
Markdown
272 lines
14 KiB
Markdown
# API Reference
|
||
|
||
## `SecureChatCompletion`
|
||
|
||
High-level OpenAI-compatible client. The recommended entry point for most use cases.
|
||
|
||
### Constructor
|
||
|
||
```typescript
|
||
new SecureChatCompletion(config?: ChatCompletionConfig)
|
||
```
|
||
|
||
#### `ChatCompletionConfig`
|
||
|
||
|
||
| 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 sent in`Authorization` header. |
|
||
| `secureMemory` | `boolean` | `true` | Enable immediate zeroing of sensitive buffers after use. |
|
||
| `timeout` | `number` | `60000` | Request timeout in milliseconds. |
|
||
| `debug` | `boolean` | `false` | Print verbose logging to the console. |
|
||
| `keyDir` | `string \| null` | `null` | Directory to load/save RSA keys on startup. If the directory contains an existing key pair it is loaded; otherwise a new pair is generated and saved there. Omit (or pass `null`) for ephemeral keys generated in memory for this session only and never written to disk — this is the default, matching the Python SDK. Node.js only; browsers are always ephemeral. |
|
||
| `keyRotationInterval` | `number` | `86400000` (24 h) | Auto-rotate RSA keys every N milliseconds. Set to`0` to disable. |
|
||
| `keyRotationDir` | `string` | value of `keyDir` | Directory where rotated key files are saved. When neither this nor `keyDir` is set, rotated keys stay in memory. Node.js only. |
|
||
| `keyRotationPassword` | `string` | `undefined` | Password used to encrypt rotated key files. |
|
||
| `maxRetries` | `number` | `2` | Maximum extra attempts on retryable errors (429, 500, 502, 503, 504, network errors). Uses exponential backoff (1 s, 2 s, …). Set to`0` to disable retries. |
|
||
|
||
### Methods
|
||
|
||
#### `create(request): Promise<ChatCompletionResponse>`
|
||
|
||
Send an encrypted chat completion request. Returns the decrypted response.
|
||
|
||
```typescript
|
||
async create(request: ChatCompletionRequest): Promise<ChatCompletionResponse>
|
||
```
|
||
|
||
**`ChatCompletionRequest` fields:**
|
||
|
||
|
||
| Field | Type | Description |
|
||
| --------------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
||
| `model` | `string` | **Required.** Model ID (see [Models](models.md)). |
|
||
| `messages` | `Message[]` | **Required.** Conversation history. |
|
||
| `temperature` | `number` | Sampling temperature (0–2). |
|
||
| `top_p` | `number` | Nucleus sampling. |
|
||
| `max_tokens` | `number` | Maximum tokens to generate. |
|
||
| `stop` | `string | string[]` | Stop sequences. |
|
||
| `n` | `number` | Number of completions to generate. |
|
||
| `stream` | `boolean` | Ignored server-side (encryption requires full response). |
|
||
| `presence_penalty` | `number` | Presence penalty (−2.0–2.0). |
|
||
| `frequency_penalty` | `number` | Frequency penalty (−2.0–2.0). |
|
||
| `logit_bias` | `Record<string, number>` | Token bias map. |
|
||
| `user` | `string` | End-user identifier (passed through). |
|
||
| `tools` | `Tool[]` | Tool/function definitions. |
|
||
| `tool_choice` | `ToolChoice` | Tool selection strategy (`"auto"`, `"none"`, `"required"`, or specific tool). |
|
||
| `security_tier` | `string` | NOMYO-specific.`"standard"` \| `"high"` \| `"maximum"`. Not encrypted into the payload. |
|
||
| `api_key` | `string` | NOMYO-specific. Per-request API key override. Not encrypted into the payload. |
|
||
| `base_url` | `string` | NOMYO-specific. Per-request router URL override. Creates a temporary client for this one call. Not encrypted into the payload. |
|
||
|
||
**Response shape (`ChatCompletionResponse`):**
|
||
|
||
```typescript
|
||
{
|
||
id: string;
|
||
object: 'chat.completion';
|
||
created: number;
|
||
model: string;
|
||
choices: Array<{
|
||
index: number;
|
||
message: {
|
||
role: string;
|
||
content: string;
|
||
tool_calls?: ToolCall[]; // present if tools were invoked
|
||
reasoning_content?: string; // chain-of-thought (Qwen3, DeepSeek-R1, etc.)
|
||
};
|
||
finish_reason: 'stop' | 'length' | 'tool_calls' | 'content_filter' | null;
|
||
}>;
|
||
usage?: {
|
||
prompt_tokens: number;
|
||
completion_tokens: number;
|
||
total_tokens: number;
|
||
};
|
||
_metadata?: {
|
||
payload_id: string; // echoes the X-Payload-ID sent with the request
|
||
processed_at: number; // Unix timestamp of server-side processing
|
||
is_encrypted: boolean; // always true for this endpoint
|
||
encryption_algorithm: string; // e.g. "hybrid-aes256-rsa4096"
|
||
response_status: string; // "success" on success
|
||
security_tier?: string; // active tier used by the server
|
||
memory_protection?: {
|
||
platform: string;
|
||
memory_locking: boolean;
|
||
secure_zeroing: boolean;
|
||
core_dump_prevention: boolean;
|
||
};
|
||
cuda_device?: {
|
||
available: boolean;
|
||
device_hash: string; // SHA-256 of device name (not the raw name)
|
||
};
|
||
};
|
||
}
|
||
```
|
||
|
||
#### `acreate(request): Promise<ChatCompletionResponse>`
|
||
|
||
Alias for `create()`. Provided for code that follows the OpenAI SDK naming convention.
|
||
|
||
#### `dispose(): void`
|
||
|
||
Stop the key-rotation timer and sever in-memory RSA key references so they can be garbage-collected. After calling `dispose()`, all methods throw `DisposedError`.
|
||
|
||
```javascript
|
||
client.dispose();
|
||
```
|
||
|
||
---
|
||
|
||
## `SecureCompletionClient`
|
||
|
||
Lower-level client that exposes key management and individual encryption/decryption operations.
|
||
Use this when you need fine-grained control; for most use cases prefer `SecureChatCompletion`.
|
||
|
||
### Constructor
|
||
|
||
```typescript
|
||
new SecureCompletionClient(config?: ClientConfig)
|
||
```
|
||
|
||
#### `ClientConfig`
|
||
|
||
All options from `ChatCompletionConfig`, plus:
|
||
|
||
|
||
| Option | Type | Default | Description |
|
||
| ------------- | --------------- | -------------------------------- | --------------------------------------------------------------- |
|
||
| `routerUrl` | `string` | `'https://api.nomyo.ai'` | NOMYO router base URL. |
|
||
| `keySize` | `2048 | 4096` | `4096` | RSA modulus length. 2048 is accepted but 4096 is recommended. |
|
||
|
||
(`baseUrl` is renamed to `routerUrl` at this level; all other options are identical.)
|
||
|
||
### Methods
|
||
|
||
#### `generateKeys(options?): Promise<void>`
|
||
|
||
Generate a fresh RSA key pair.
|
||
|
||
```typescript
|
||
await client.generateKeys({
|
||
keySize?: 2048 | 4096, // default: 4096
|
||
saveToFile?: boolean, // default: false
|
||
keyDir?: string, // default: 'client_keys'
|
||
password?: string, // minimum 8 characters if provided
|
||
});
|
||
```
|
||
|
||
#### `loadKeys(privateKeyPath, publicKeyPath?, password?): Promise<void>`
|
||
|
||
Load an existing key pair from PEM files. Node.js only.
|
||
|
||
```typescript
|
||
await client.loadKeys(
|
||
'client_keys/private_key.pem',
|
||
'client_keys/public_key.pem', // optional; derived from private key path if omitted
|
||
'your-password' // required if private key is encrypted
|
||
);
|
||
```
|
||
|
||
#### `fetchServerPublicKey(): Promise<string>`
|
||
|
||
Fetch the server's RSA public key from `/pki/public_key` over HTTPS. Called automatically on every encryption; exposed for diagnostics.
|
||
|
||
#### `encryptPayload(payload): Promise<ArrayBuffer>`
|
||
|
||
Encrypt a request payload. Returns the encrypted binary package ready to POST.
|
||
|
||
#### `decryptResponse(encrypted, payloadId): Promise<object>`
|
||
|
||
Decrypt a response body received from the secure endpoint.
|
||
|
||
#### `sendSecureRequest(payload, payloadId, apiKey?, securityTier?): Promise<object>`
|
||
|
||
Full encrypt → POST → decrypt cycle with retry logic. Called internally by `SecureChatCompletion.create()`.
|
||
|
||
#### `dispose(): void`
|
||
|
||
Same as `SecureChatCompletion.dispose()`.
|
||
|
||
---
|
||
|
||
## Secure Memory API
|
||
|
||
```typescript
|
||
import {
|
||
getMemoryProtectionInfo,
|
||
disableSecureMemory,
|
||
enableSecureMemory,
|
||
SecureByteContext,
|
||
} from 'nomyo-js';
|
||
```
|
||
|
||
### `getMemoryProtectionInfo(): ProtectionInfo`
|
||
|
||
Returns information about the memory protection available on the current platform:
|
||
|
||
```typescript
|
||
interface ProtectionInfo {
|
||
canLock: boolean; // true if mlock is available (requires native addon)
|
||
isPlatformSecure: boolean;
|
||
method: 'mlock' | 'zero-only' | 'none';
|
||
details?: string;
|
||
}
|
||
```
|
||
|
||
### `disableSecureMemory(): void`
|
||
|
||
Disable secure-memory zeroing globally. Affects new `SecureByteContext` instances that do not pass an explicit `useSecure` argument. Existing client instances are unaffected (they pass `useSecure` explicitly).
|
||
|
||
### `enableSecureMemory(): void`
|
||
|
||
Re-enable secure memory operations globally.
|
||
|
||
### `SecureByteContext`
|
||
|
||
Low-level context manager that zeros an `ArrayBuffer` in a `finally` block even if an exception occurs. Analogous to Python's `secure_bytearray()` context manager.
|
||
|
||
```typescript
|
||
const context = new SecureByteContext(sensitiveBuffer);
|
||
const result = await context.use(async (data) => {
|
||
return doSomethingWith(data);
|
||
});
|
||
// sensitiveBuffer is zeroed here regardless of whether doSomethingWith threw
|
||
```
|
||
|
||
---
|
||
|
||
## Error Classes
|
||
|
||
All errors are exported from the package root.
|
||
|
||
```typescript
|
||
import {
|
||
APIError,
|
||
AuthenticationError,
|
||
InvalidRequestError,
|
||
RateLimitError,
|
||
ForbiddenError,
|
||
ServerError,
|
||
ServiceUnavailableError,
|
||
APIConnectionError,
|
||
SecurityError,
|
||
DisposedError,
|
||
} from 'nomyo-js';
|
||
```
|
||
|
||
|
||
| Class | HTTP status | Thrown when |
|
||
| --------------------------- | ------------- | -------------------------------------------------------------- |
|
||
| `AuthenticationError` | 401 | Invalid or missing API key |
|
||
| `InvalidRequestError` | 400 | Malformed request (e.g. streaming requested) |
|
||
| `ForbiddenError` | 403 | Model not allowed for the requested security tier |
|
||
| `RateLimitError` | 429 | Rate limit exceeded (after all retries exhausted) |
|
||
| `ServerError` | 500 | Internal server error (after all retries exhausted) |
|
||
| `ServiceUnavailableError` | 503 | Inference backend unavailable (after all retries exhausted) |
|
||
| `APIError` | varies | Other HTTP errors (404, 502, 504, etc.) |
|
||
| `APIConnectionError` | — | Network failure or timeout (after all retries exhausted) |
|
||
| `SecurityError` | — | HTTPS not used, header injection detected, or crypto failure |
|
||
| `DisposedError` | — | Method called after`dispose()` |
|
||
|
||
All errors that extend `APIError` expose `statusCode?: number` and `errorDetails?: object`.
|