nomyo-js/doc/troubleshooting.md
alpha nerd 987acf8816
Some checks failed
NYX Security Scan / nyx-scan (pull_request) Failing after 5m51s
feat!: make RSA keys ephemeral by default
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>
2026-07-19 11:19:03 +02:00

314 lines
9.2 KiB
Markdown

# Troubleshooting
## Authentication Errors
### `AuthenticationError: Invalid or missing API key`
The server rejected your API key.
**Causes and fixes:**
- Key not set — pass `apiKey` to the constructor or use `process.env.NOMYO_API_KEY`.
- Key has leading/trailing whitespace — check the value with `console.log(JSON.stringify(process.env.NOMYO_API_KEY))`.
- Key contains CR or LF characters — the client rejects keys with `\r` or `\n` and throws `SecurityError` before the request is sent. Regenerate the key.
```javascript
const client = new SecureChatCompletion({
apiKey: process.env.NOMYO_API_KEY, // never hardcode
});
```
---
## Connection Errors
### `APIConnectionError: Network error` / `connect ECONNREFUSED`
The client could not reach the router.
**Check:**
1. `baseUrl` is correct — the default is `https://api.nomyo.ai` (port **12435**).
2. You have network access to the host.
3. TLS is not being blocked by a proxy or firewall.
### `SecurityError: HTTPS is required`
You passed an `http://` URL without setting `allowHttp: true`.
```javascript
// Local dev only
const client = new SecureChatCompletion({
baseUrl: 'http://localhost:12435',
allowHttp: true,
});
```
Never set `allowHttp: true` in production — the server public key fetch and all request data would travel unencrypted.
### `APIConnectionError: Request timed out`
The default timeout is 60 seconds. Larger models or busy endpoints may need more:
```javascript
const client = new SecureChatCompletion({
apiKey: process.env.NOMYO_API_KEY,
timeout: 120000, // 2 minutes
});
```
---
## Key Loading Failures
### `Error: Failed to load keys: no such file or directory`
The `keyDir` directory or the PEM files inside it don't exist. This only applies when you opt into persistence — by default keys are ephemeral and no directory is touched at all. On first run with a `keyDir` set, the library generates and saves a new key pair there automatically, so make sure the directory is writable:
```javascript
const client = new SecureChatCompletion({
apiKey: process.env.NOMYO_API_KEY,
keyDir: '/var/lib/myapp/nomyo-keys', // directory must exist and be writable
});
```
### `Error: Invalid passphrase` / `Error: Failed to decrypt private key`
The password you passed to `loadKeys()` or `keyRotationPassword` doesn't match what was used to encrypt the file.
```javascript
await client.loadKeys(
'client_keys/private_key.pem',
'client_keys/public_key.pem',
process.env.NOMYO_KEY_PASSWORD, // must match the password used on generateKeys()
);
```
### `Error: RSA key too small`
The library enforces a minimum key size of 2048 bits. If you have old 1024-bit keys, regenerate them:
```javascript
await client.generateKeys({
saveToFile: true,
keyDir: 'client_keys',
keySize: 4096, // recommended
});
```
### `Error: Failed to load keys` (browser)
Key loading from files is a Node.js-only feature. In browsers, keys are generated in memory on first use. Do not call `loadKeys()` in a browser context.
---
## Rate Limit Errors
### `RateLimitError: Rate limit exceeded`
All automatic retries were exhausted. The default limit is 2 requests/second; burst allows 4 requests/second once per 10-second window.
**Fixes:**
- Reduce concurrency — avoid large `Promise.all` batches.
- Add client-side throttling (see [Rate Limits](rate-limits.md)).
- Increase `maxRetries` so the client backs off longer before giving up:
```javascript
const client = new SecureChatCompletion({
apiKey: process.env.NOMYO_API_KEY,
maxRetries: 5,
});
```
### `ServiceUnavailableError` with 30-minute cool-down
Burst limits were hit repeatedly and a cool-down was applied to your key. Wait 30 minutes, then review your request patterns.
---
## Model / Tier Errors
### `ForbiddenError: Model not allowed for this security tier`
The model you requested is not available at the security tier you specified. Try a lower tier or a different model:
```javascript
// If 'maximum' tier rejects the model, try 'high' or 'standard'
const response = await client.create({
model: 'Qwen/Qwen3.5-27B',
messages: [...],
security_tier: 'high', // try 'standard' if still rejected
});
```
See [Models — Security Tier Compatibility](models.md#security-tier-compatibility) for details.
---
## Crypto / Security Errors
### `SecurityError: Decryption failed`
The response could not be decrypted. This is intentionally vague to avoid leaking crypto details.
**Possible causes:**
- The server returned a malformed response (check `debug: true` output).
- A network proxy modified the response body.
- The server's public key changed mid-session — the next request will re-fetch it automatically.
Enable debug mode to log the raw response and narrow the cause:
```javascript
const client = new SecureChatCompletion({
apiKey: process.env.NOMYO_API_KEY,
debug: true,
});
```
### `Error: Unsupported protocol version` / `Error: Unsupported encryption algorithm`
The server sent a response in a protocol version or with an encryption algorithm not supported by this client version. Update the package:
```bash
npm update nomyo-js
```
---
## `DisposedError`: Method called after `dispose()`
You called a method on a client that has already been disposed.
```javascript
client.dispose();
await client.create(...); // throws DisposedError
```
Create a new client instance if you need to make more requests after disposal.
---
## Memory Protection Warnings
### `getMemoryProtectionInfo()` returns `method: 'zero-only'`
This is normal for a pure JavaScript installation. The library zeroes sensitive buffers immediately after use but cannot lock pages to prevent swapping (OS `mlock` requires a native addon).
```javascript
import { getMemoryProtectionInfo } from 'nomyo-js';
const info = getMemoryProtectionInfo();
// { method: 'zero-only', canLock: false, isPlatformSecure: false }
```
For environments where swap-file exposure is unacceptable (HIPAA PHI, classified data), install the optional `nomyo-native` addon or run on a system with swap disabled.
---
## Node.js-Specific Issues
### `ReferenceError: crypto is not defined`
In CommonJS modules on Node.js before v19, `crypto` is not a global. Import it explicitly:
```javascript
// CommonJS
const { webcrypto } = require('crypto');
global.crypto = webcrypto;
// Or switch to ES modules (recommended)
// package.json: "type": "module"
```
The library itself imports `crypto` correctly — this error only appears if your own application code tries to use `crypto` directly.
### `SyntaxError: Cannot use import statement in a module` / CommonJS vs ESM
The package ships both CommonJS (`dist/node/`) and ESM (`dist/esm/`) builds. Node.js selects the correct one automatically via `package.json` `exports`. If you see import errors, check that your `package.json` or bundler is not forcing the wrong format.
For ESM: set `"type": "module"` in your `package.json` or use `.mjs` file extensions.
For CommonJS: use `require('nomyo-js')` or `.cjs` extensions.
### TypeScript: `Cannot find module 'nomyo-js'` / missing types
Ensure your `tsconfig.json` includes `"moduleResolution": "bundler"` or `"moduleResolution": "node16"` and that `nomyo-js` is in `dependencies` (not just `devDependencies`):
```bash
npm install nomyo-js
```
---
## Browser-Specific Issues
### `Content Security Policy blocked`
If your app's CSP restricts `script-src` or `connect-src`, add the NOMYO API domain:
```
Content-Security-Policy: connect-src https://api.nomyo.ai;
```
### `TypeError: Failed to fetch` (CORS)
The NOMYO API includes CORS headers. If you see CORS errors in a browser, verify the `baseUrl` is correct (HTTPS, correct port) and that no browser extension is blocking the request.
### Keys not persisted across page reloads
This is expected behaviour — browsers do not have file system access. Keys are generated fresh on each page load. If you need persistent keys in a browser context, implement your own `loadKeys`/`generateKeys` wrapper using `localStorage` or `IndexedDB` (not recommended for high-security scenarios).
---
## Debugging Tips
### Enable verbose logging
```javascript
const client = new SecureChatCompletion({
apiKey: process.env.NOMYO_API_KEY,
debug: true,
});
```
Debug mode logs: key generation/loading, server public key fetches, request encryption details, retry attempts, and response decryption.
### Check memory protection status
```javascript
import { getMemoryProtectionInfo } from 'nomyo-js';
console.log(getMemoryProtectionInfo());
```
### Inspect response metadata
The `_metadata` field in every response carries server-side diagnostics:
```javascript
const response = await client.create({ ... });
console.log(response._metadata);
// {
// payload_id: '...',
// is_encrypted: true,
// encryption_algorithm: 'hybrid-aes256-rsa4096',
// security_tier: 'standard',
// memory_protection: { ... },
// }
```
### Test with minimum configuration
Strip all optional configuration and test with the simplest possible call to isolate the issue:
```javascript
import { SecureChatCompletion } from 'nomyo-js';
const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY });
const r = await client.create({
model: 'Qwen/Qwen3-0.6B',
messages: [{ role: 'user', content: 'ping' }],
});
console.log(r.choices[0].message.content);
client.dispose();
```