# Security Guide ## Overview NOMYO.js provides end-to-end encryption for all communication between your application and NOMYO inference endpoints. Your prompts and responses are encrypted before leaving your process — the inference server never processes plaintext. For the full cryptographic architecture and threat model see [SECURITY.md](SECURITY.md). --- ## Encryption Mechanism ### Hybrid Encryption Each request uses a two-layer scheme: 1. **AES-256-GCM** encrypts the payload (authenticated encryption — prevents tampering). 2. **RSA-OAEP-SHA256** wraps the AES key for secure key exchange. The server holds the RSA private key; your client generates the AES key fresh for every request. ### Per-Request Ephemeral AES Keys - A new 256-bit AES key is generated for every `create()` call using the Web Crypto API. - The key is never reused — forward secrecy is ensured per request. - The key is zeroed from memory immediately after encryption. ### Key Exchange Your client's RSA public key is sent in the `X-Public-Key` request header. The server encrypts the response with it so only your client can decrypt the reply. --- ## Memory Protection ### What the Library Does All intermediate sensitive buffers (AES key, plaintext payload, decrypted response bytes) are wrapped in `SecureByteContext`. This guarantees they are zeroed in a `finally` block immediately after use, even if an exception occurs. The encrypted request body (`ArrayBuffer`) is also zeroed by the Node.js HTTP client after the data is handed to the socket. ### Limitations (Pure JavaScript) JavaScript has no direct access to OS memory management. The library cannot: - Lock pages to prevent swapping (`mlock` / `VirtualLock`) - Prevent the garbage collector from copying data internally - Guarantee memory won't appear in core dumps **Impact:** On a system under memory pressure, sensitive data could briefly be written to swap. For environments where this is unacceptable (PHI, classified), install the optional native addon or run on a system with swap disabled. ### Native Addon (Optional) The `nomyo-native` addon adds true `mlock` support. When installed, `getMemoryProtectionInfo()` reports `method: 'mlock'` and `canLock: true`: ```javascript import { getMemoryProtectionInfo } from 'nomyo-js'; const info = getMemoryProtectionInfo(); // Without addon: { method: 'zero-only', canLock: false } // With addon: { method: 'mlock', canLock: true } ``` --- ## Minimise Response Lifetime The library protects all intermediate crypto material in secure memory. However, the **final parsed response object** is returned to your code, and you are responsible for how long it lives. ```javascript // GOOD — extract what you need, then drop the response immediately const response = await client.create({ model: 'Qwen/Qwen3.5-9B', messages: [{ role: 'user', content: 'Summarise patient record #1234' }], security_tier: 'maximum', }); const reply = response.choices[0].message.content; // Let response go out of scope here — don't hold it in a variable // longer than necessary // BAD — holding the full response object in a long-lived scope this.lastResponse = response; // stored for minutes / hours ``` JavaScript's `delete` and variable reassignment do not zero the underlying memory. For sensitive data (PHI, classified), process and discard as quickly as possible — do not store in class attributes, global caches, or log files. --- ## Key Management ### Default Behaviour Keys are automatically generated on first use and saved to `client_keys/` (Node.js). On subsequent runs the saved keys are reloaded automatically. ``` client_keys/ private_key.pem # permissions 0600 (owner-only) public_key.pem # permissions 0644 ``` ### Configure the Key Directory ```javascript const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY, keyDir: '/etc/myapp/nomyo-keys', // custom path, outside project directory }); ``` ### Password-Protected Keys (Recommended for Production) Protect key files with a password so they cannot be used even if the file is leaked: ```javascript import { SecureCompletionClient } from 'nomyo-js'; const client = new SecureCompletionClient({ routerUrl: 'https://api.nomyo.ai' }); await client.generateKeys({ saveToFile: true, keyDir: 'client_keys', password: process.env.NOMYO_KEY_PASSWORD, // minimum 8 characters }); ``` To load password-protected keys manually: ```javascript await client.loadKeys( 'client_keys/private_key.pem', 'client_keys/public_key.pem', process.env.NOMYO_KEY_PASSWORD ); ``` ### Key Rotation Keys rotate automatically every 24 hours by default. Configure or disable: ```javascript const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY, keyRotationInterval: 3600000, // rotate every hour keyRotationDir: '/var/lib/myapp/keys', keyRotationPassword: process.env.KEY_PWD, }); // Or disable entirely for short-lived processes const client2 = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY, keyRotationInterval: 0, }); ``` ### File Permissions Private key files are saved with `0600` permissions (owner read/write only) on Unix-like systems. Add `client_keys/` and `*.pem` to your `.gitignore` — both are already included if you use this package's default `.gitignore`. --- ## Security Tiers | Tier | Hardware | Use case | |------|----------|----------| | `"standard"` | GPU | General secure inference | | `"high"` | CPU/GPU balanced | Sensitive business data, enforces secure tokenizer | | `"maximum"` | CPU only | HIPAA PHI, classified data — maximum isolation | Higher tiers add round-trip latency but increase hardware-level isolation. --- ## HTTPS Enforcement The client enforces HTTPS by default. HTTP connections require explicit opt-in and print a visible warning: ```javascript // Production — HTTPS only (default) const client = new SecureChatCompletion({ baseUrl: 'https://api.nomyo.ai' }); // Local development — HTTP allowed with explicit flag const devClient = new SecureChatCompletion({ baseUrl: 'http://localhost:12435', allowHttp: true, // prints: "WARNING: Using HTTP instead of HTTPS..." }); ``` Without `allowHttp: true`, connecting over HTTP throws `SecurityError`. The server's public key is fetched over HTTPS with TLS certificate verification to prevent man-in-the-middle attacks. --- ## API Key Security API keys are sent as `Bearer` tokens in the `Authorization` header. The client validates that the key does not contain CR or LF characters to prevent HTTP header injection. Never hardcode API keys in source code — use environment variables: ```javascript const client = new SecureChatCompletion({ apiKey: process.env.NOMYO_API_KEY, }); ``` --- ## Production Checklist - [ ] Always use HTTPS (`allowHttp` is `false` by default) - [ ] Load API key from environment variable, not hardcoded - [ ] Enable `secureMemory: true` (default) - [ ] Use password-protected key files (`keyRotationPassword`) - [ ] Store keys outside the project directory and outside version control - [ ] Add `client_keys/` and `*.pem` to `.gitignore` - [ ] Call `client.dispose()` when the client is no longer needed - [ ] Consider the native addon if swap-file exposure is unacceptable --- ## Compliance Considerations ### HIPAA For Protected Health Information (PHI): - Use `security_tier: 'maximum'` on requests containing PHI - Enable password-protected key files - Ensure HTTPS is enforced (the default) - Minimise response lifetime in memory (extract, use, discard) ### Data Classification | Classification | Recommended tier | |---------------|-----------------| | Public / internal | `"standard"` | | Confidential business data | `"high"` | | PHI, PII, classified | `"maximum"` |