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>
10 KiB
Troubleshooting
Authentication Errors
AuthenticationError: Invalid or missing API key
The server rejected your API key.
Causes and fixes:
- Key not set — pass
apiKeyto the constructor or useprocess.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
\ror\nand throwsSecurityErrorbefore the request is sent. Regenerate the key.
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:
baseUrlis correct — the default ishttps://api.nomyo.ai(port 12435).- You have network access to the host.
- TLS is not being blocked by a proxy or firewall.
SecurityError: HTTPS is required
You passed an http:// URL without setting allowHttp: true.
// 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 900 seconds (15 minutes), matching the Python SDK. Encrypted inference cannot stream, so the entire completion arrives in a single response. Lower it if you want to fail faster:
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:
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.
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:
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.allbatches. - Add client-side throttling (see Rate Limits).
- Increase
maxRetriesso the client backs off longer before giving up:
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:
// 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 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: trueoutput). - 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:
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:
npm update nomyo-js
DisposedError: Method called after dispose()
You called a method on a client that has already been disposed.
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).
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.
method: 'zero-only' even with the native addon installed
canLock is probed, not assumed — the addon being loaded does not mean the OS
will grant locks. mlock is commonly refused by RLIMIT_MEMLOCK, which
defaults to a few megabytes and is 0 in some container runtimes. When that
happens details says so explicitly, and buffers are still zeroed.
ulimit -l # current memlock limit in KB; "unlimited" or a large value is what you want
Raise the limit (ulimit -l, systemd LimitMEMLOCK=, or Docker
--ulimit memlock=-1), or grant CAP_IPC_LOCK.
Note also that npm 11 gates package install scripts, so a fresh npm ci may skip
building the addon entirely until you run npm approve-scripts.
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:
// 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):
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
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
import { getMemoryProtectionInfo } from 'nomyo-js';
console.log(getMemoryProtectionInfo());
Inspect response metadata
The _metadata field in every response carries server-side diagnostics:
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:
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();