2026-04-16 16:44:26 +02:00
# 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. |
feat: align timeout, error types and docs with the Python SDK
Completes the parity work (step 4).
Request timeout now defaults to 900 s, matching Python, instead of 60 s.
Encrypted inference cannot stream, so an entire completion arrives in one
response; a long generation on a busy backend legitimately takes minutes
and was timing out here while succeeding in the Python client.
Error types now distinguish malformed data from integrity failures.
Python raises ValueError for a bad package, a non-200 or unparseable
/pki/public_key, and plaintext that will not parse, reserving
SecurityError for crypto failures. This port wrapped nearly all of it in
SecurityError — so a server sending malformed JSON was reported as an
authentication failure, pointing debugging in exactly the wrong
direction. Malformed data is now a plain Error (the JS equivalent of
ValueError), carried past the deliberately opaque catch-all by a symbol
marker rather than a new exported class. Genuine crypto failures still
report a single vague message so they cannot serve as a decryption
oracle.
Also adds the missing guard Python has: decrypting without a private key
now says so, instead of failing later and being reported as an integrity
failure.
doc/attestation.md ports the Python attestation guide to the JS API, and
documents the two deliberate divergences: no verify_ssl escape hatch, and
jose injection instead of a runtime dynamic import.
Version 0.1.0 -> 0.3.0 to match the Python client's feature level, now
that the two are at parity.
Not ported: Python's warning when secure_memory=True but the SecureMemory
module is unavailable. There is no JS equivalent — zeroing is always
available, and the weaker case (mlock unavailable) is already reported
honestly by getProtectionInfo().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:17:52 +02:00
| `timeout` | `number` | `900000` (15 min) | Request timeout in milliseconds. Encrypted inference cannot stream, so the whole completion arrives in one response; long generations legitimately take minutes. |
2026-04-16 16:44:26 +02:00
| `debug` | `boolean` | `false` | Print verbose logging to the console. |
2026-07-19 11:19:03 +02:00
| `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. |
2026-04-16 16:44:26 +02:00
| `keyRotationInterval` | `number` | `86400000` (24 h) | Auto-rotate RSA keys every N milliseconds. Set to`0` to disable. |
2026-07-19 11:19:03 +02:00
| `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. |
2026-04-16 16:44:26 +02:00
| `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. |
feat: align timeout, error types and docs with the Python SDK
Completes the parity work (step 4).
Request timeout now defaults to 900 s, matching Python, instead of 60 s.
Encrypted inference cannot stream, so an entire completion arrives in one
response; a long generation on a busy backend legitimately takes minutes
and was timing out here while succeeding in the Python client.
Error types now distinguish malformed data from integrity failures.
Python raises ValueError for a bad package, a non-200 or unparseable
/pki/public_key, and plaintext that will not parse, reserving
SecurityError for crypto failures. This port wrapped nearly all of it in
SecurityError — so a server sending malformed JSON was reported as an
authentication failure, pointing debugging in exactly the wrong
direction. Malformed data is now a plain Error (the JS equivalent of
ValueError), carried past the deliberately opaque catch-all by a symbol
marker rather than a new exported class. Genuine crypto failures still
report a single vague message so they cannot serve as a decryption
oracle.
Also adds the missing guard Python has: decrypting without a private key
now says so, instead of failing later and being reported as an integrity
failure.
doc/attestation.md ports the Python attestation guide to the JS API, and
documents the two deliberate divergences: no verify_ssl escape hatch, and
jose injection instead of a runtime dynamic import.
Version 0.1.0 -> 0.3.0 to match the Python client's feature level, now
that the two are at parity.
Not ported: Python's warning when secure_memory=True but the SecureMemory
module is unavailable. There is no JS equivalent — zeroing is always
available, and the weaker case (mlock unavailable) is already reported
honestly by getProtectionInfo().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:17:52 +02:00
| `attestationPolicy` | `AttestationPolicy` | `undefined` | Enables SGX attestation verification before any plaintext is sent. Omit to disable. See [Attestation ](attestation.md ). |
| `quoteVerifier` | `QuoteVerifier` | `undefined` | Performs the DCAP quote verification (e.g.`JwtQuoteVerifier` ). Required for a policy to actually verify; without one, a set policy treats attestation as unverifiable. |
2026-04-16 16:44:26 +02:00
### 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,
feat: align timeout, error types and docs with the Python SDK
Completes the parity work (step 4).
Request timeout now defaults to 900 s, matching Python, instead of 60 s.
Encrypted inference cannot stream, so an entire completion arrives in one
response; a long generation on a busy backend legitimately takes minutes
and was timing out here while succeeding in the Python client.
Error types now distinguish malformed data from integrity failures.
Python raises ValueError for a bad package, a non-200 or unparseable
/pki/public_key, and plaintext that will not parse, reserving
SecurityError for crypto failures. This port wrapped nearly all of it in
SecurityError — so a server sending malformed JSON was reported as an
authentication failure, pointing debugging in exactly the wrong
direction. Malformed data is now a plain Error (the JS equivalent of
ValueError), carried past the deliberately opaque catch-all by a symbol
marker rather than a new exported class. Genuine crypto failures still
report a single vague message so they cannot serve as a decryption
oracle.
Also adds the missing guard Python has: decrypting without a private key
now says so, instead of failing later and being reported as an integrity
failure.
doc/attestation.md ports the Python attestation guide to the JS API, and
documents the two deliberate divergences: no verify_ssl escape hatch, and
jose injection instead of a runtime dynamic import.
Version 0.1.0 -> 0.3.0 to match the Python client's feature level, now
that the two are at parity.
Not ported: Python's warning when secure_memory=True but the SecureMemory
module is unavailable. There is no JS equivalent — zeroing is always
available, and the weaker case (mlock unavailable) is already reported
honestly by getProtectionInfo().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:17:52 +02:00
AttestationError,
2026-04-16 16:44:26 +02:00
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 |
feat: align timeout, error types and docs with the Python SDK
Completes the parity work (step 4).
Request timeout now defaults to 900 s, matching Python, instead of 60 s.
Encrypted inference cannot stream, so an entire completion arrives in one
response; a long generation on a busy backend legitimately takes minutes
and was timing out here while succeeding in the Python client.
Error types now distinguish malformed data from integrity failures.
Python raises ValueError for a bad package, a non-200 or unparseable
/pki/public_key, and plaintext that will not parse, reserving
SecurityError for crypto failures. This port wrapped nearly all of it in
SecurityError — so a server sending malformed JSON was reported as an
authentication failure, pointing debugging in exactly the wrong
direction. Malformed data is now a plain Error (the JS equivalent of
ValueError), carried past the deliberately opaque catch-all by a symbol
marker rather than a new exported class. Genuine crypto failures still
report a single vague message so they cannot serve as a decryption
oracle.
Also adds the missing guard Python has: decrypting without a private key
now says so, instead of failing later and being reported as an integrity
failure.
doc/attestation.md ports the Python attestation guide to the JS API, and
documents the two deliberate divergences: no verify_ssl escape hatch, and
jose injection instead of a runtime dynamic import.
Version 0.1.0 -> 0.3.0 to match the Python client's feature level, now
that the two are at parity.
Not ported: Python's warning when secure_memory=True but the SecureMemory
module is unavailable. There is no JS equivalent — zeroing is always
available, and the weaker case (mlock unavailable) is already reported
honestly by getProtectionInfo().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:17:52 +02:00
| `AttestationError` | — | SGX attestation failed under`enforce` (extends `SecurityError` ) |
2026-04-16 16:44:26 +02:00
| `DisposedError` | — | Method called after`dispose()` |
All errors that extend `APIError` expose `statusCode?: number` and `errorDetails?: object` .
feat: align timeout, error types and docs with the Python SDK
Completes the parity work (step 4).
Request timeout now defaults to 900 s, matching Python, instead of 60 s.
Encrypted inference cannot stream, so an entire completion arrives in one
response; a long generation on a busy backend legitimately takes minutes
and was timing out here while succeeding in the Python client.
Error types now distinguish malformed data from integrity failures.
Python raises ValueError for a bad package, a non-200 or unparseable
/pki/public_key, and plaintext that will not parse, reserving
SecurityError for crypto failures. This port wrapped nearly all of it in
SecurityError — so a server sending malformed JSON was reported as an
authentication failure, pointing debugging in exactly the wrong
direction. Malformed data is now a plain Error (the JS equivalent of
ValueError), carried past the deliberately opaque catch-all by a symbol
marker rather than a new exported class. Genuine crypto failures still
report a single vague message so they cannot serve as a decryption
oracle.
Also adds the missing guard Python has: decrypting without a private key
now says so, instead of failing later and being reported as an integrity
failure.
doc/attestation.md ports the Python attestation guide to the JS API, and
documents the two deliberate divergences: no verify_ssl escape hatch, and
jose injection instead of a runtime dynamic import.
Version 0.1.0 -> 0.3.0 to match the Python client's feature level, now
that the two are at parity.
Not ported: Python's warning when secure_memory=True but the SecureMemory
module is unavailable. There is no JS equivalent — zeroing is always
available, and the weaker case (mlock unavailable) is already reported
honestly by getProtectionInfo().
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 12:17:52 +02:00
### Malformed data vs security failures
A plain `Error` — not a `SecurityError` — is thrown when a response is
structurally wrong rather than cryptographically suspect: a missing package
field, an unsupported protocol version, a non-200 or unparseable
`/pki/public_key` response, or plaintext that decrypted and authenticated
successfully but is not valid JSON.
`SecurityError` is reserved for actual security conditions, and stays
deliberately vague about which stage failed so it cannot be used as a decryption
oracle:
```typescript
try {
await client.create({ model, messages });
} catch (error) {
if (error instanceof SecurityError) {
// integrity/authentication failure, HTTPS violation, or failed attestation
} else if (error instanceof APIError) {
// server responded with an HTTP error
} else {
// malformed response data
}
}
```
This mirrors the Python SDK, which raises `ValueError` where this client throws
a plain `Error` .