doc: update for latest changes
This commit is contained in:
parent
d9d2ec98db
commit
3b1792e613
2 changed files with 77 additions and 17 deletions
61
README.md
61
README.md
|
|
@ -81,8 +81,10 @@ console.log(response.choices[0].message.content);
|
||||||
- **Automatic key loading**: Existing keys are loaded automatically from `client_keys/` directory (Node.js only)
|
- **Automatic key loading**: Existing keys are loaded automatically from `client_keys/` directory (Node.js only)
|
||||||
- **No manual intervention required**: The library handles key management automatically
|
- **No manual intervention required**: The library handles key management automatically
|
||||||
- **Optional persistence**: Keys can be saved to `client_keys/` directory for reuse across sessions (Node.js only)
|
- **Optional persistence**: Keys can be saved to `client_keys/` directory for reuse across sessions (Node.js only)
|
||||||
- **Password protection**: Optional password encryption for private keys (recommended for production)
|
- **Password protection**: Optional password encryption for private keys (minimum 8 characters required)
|
||||||
- **Secure permissions**: Private keys stored with restricted permissions (600 - owner-only access)
|
- **Secure permissions**: Private keys stored with restricted permissions (600 - owner-only access)
|
||||||
|
- **Automatic key rotation**: Keys are rotated on a configurable interval (default: 24 hours) to limit fingerprint lifetime
|
||||||
|
- **Explicit lifecycle management**: Call `dispose()` to immediately zero in-memory key material and stop the rotation timer
|
||||||
|
|
||||||
### Secure Memory Protection
|
### Secure Memory Protection
|
||||||
|
|
||||||
|
|
@ -242,11 +244,38 @@ await client.loadKeys(
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Resource Management
|
||||||
|
|
||||||
|
Always call `dispose()` when finished to zero key material and stop the background rotation timer:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const client = new SecureChatCompletion({
|
||||||
|
baseUrl: 'https://api.nomyo.ai:12434',
|
||||||
|
keyRotationInterval: 3600000, // rotate every hour
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await client.create({ model: 'Qwen/Qwen3-0.6B', messages: [...] });
|
||||||
|
console.log(response.choices[0].message.content);
|
||||||
|
} finally {
|
||||||
|
client.dispose();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To disable key rotation entirely (e.g. short-lived scripts):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const client = new SecureChatCompletion({
|
||||||
|
baseUrl: 'https://api.nomyo.ai:12434',
|
||||||
|
keyRotationInterval: 0, // disabled
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## 🧪 Platform Support
|
## 🧪 Platform Support
|
||||||
|
|
||||||
### Node.js
|
### Node.js
|
||||||
|
|
||||||
- **Minimum version**: Node.js 15+ (for `crypto.webcrypto`)
|
- **Minimum version**: Node.js 14.17+
|
||||||
- **Recommended**: Node.js 18 LTS or later
|
- **Recommended**: Node.js 18 LTS or later
|
||||||
- **Key storage**: File system (`client_keys/` directory)
|
- **Key storage**: File system (`client_keys/` directory)
|
||||||
- **Security**: Full implementation with automatic key persistence
|
- **Security**: Full implementation with automatic key persistence
|
||||||
|
|
@ -269,10 +298,15 @@ await client.loadKeys(
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
new SecureChatCompletion(config?: {
|
new SecureChatCompletion(config?: {
|
||||||
baseUrl?: string; // Default: 'https://api.nomyo.ai:12434'
|
baseUrl?: string; // Default: 'https://api.nomyo.ai:12434'
|
||||||
allowHttp?: boolean; // Default: false
|
allowHttp?: boolean; // Default: false
|
||||||
apiKey?: string; // Default: undefined
|
apiKey?: string; // Default: undefined
|
||||||
secureMemory?: boolean; // Default: true
|
secureMemory?: boolean; // Default: true
|
||||||
|
timeout?: number; // Request timeout ms. Default: 60000
|
||||||
|
debug?: boolean; // Enable verbose logging. Default: false
|
||||||
|
keyRotationInterval?: number; // Key rotation ms. 0 = disabled. Default: 86400000 (24h)
|
||||||
|
keyRotationDir?: string; // Directory for rotated keys. Default: 'client_keys'
|
||||||
|
keyRotationPassword?: string; // Password for rotated key files
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -280,6 +314,7 @@ new SecureChatCompletion(config?: {
|
||||||
|
|
||||||
- `create(request: ChatCompletionRequest): Promise<ChatCompletionResponse>`
|
- `create(request: ChatCompletionRequest): Promise<ChatCompletionResponse>`
|
||||||
- `acreate(request: ChatCompletionRequest): Promise<ChatCompletionResponse>` (alias)
|
- `acreate(request: ChatCompletionRequest): Promise<ChatCompletionResponse>` (alias)
|
||||||
|
- `dispose(): void` — zero key material and stop rotation timer
|
||||||
|
|
||||||
### SecureCompletionClient
|
### SecureCompletionClient
|
||||||
|
|
||||||
|
|
@ -289,10 +324,15 @@ Lower-level API for advanced use cases.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
new SecureCompletionClient(config?: {
|
new SecureCompletionClient(config?: {
|
||||||
routerUrl?: string; // Default: 'https://api.nomyo.ai:12434'
|
routerUrl?: string; // Default: 'https://api.nomyo.ai:12434'
|
||||||
allowHttp?: boolean; // Default: false
|
allowHttp?: boolean; // Default: false
|
||||||
secureMemory?: boolean; // Default: true
|
secureMemory?: boolean; // Default: true
|
||||||
keySize?: 2048 | 4096; // Default: 4096
|
keySize?: 2048 | 4096; // Default: 4096
|
||||||
|
timeout?: number; // Request timeout ms. Default: 60000
|
||||||
|
debug?: boolean; // Enable verbose logging. Default: false
|
||||||
|
keyRotationInterval?: number; // Key rotation ms. 0 = disabled. Default: 86400000 (24h)
|
||||||
|
keyRotationDir?: string; // Directory for rotated keys. Default: 'client_keys'
|
||||||
|
keyRotationPassword?: string; // Password for rotated key files
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -304,6 +344,7 @@ new SecureCompletionClient(config?: {
|
||||||
- `encryptPayload(payload: object): Promise<ArrayBuffer>`
|
- `encryptPayload(payload: object): Promise<ArrayBuffer>`
|
||||||
- `decryptResponse(encrypted: ArrayBuffer, payloadId: string): Promise<object>`
|
- `decryptResponse(encrypted: ArrayBuffer, payloadId: string): Promise<object>`
|
||||||
- `sendSecureRequest(payload: object, payloadId: string, apiKey?: string): Promise<object>`
|
- `sendSecureRequest(payload: object, payloadId: string, apiKey?: string): Promise<object>`
|
||||||
|
- `dispose(): void` — zero key material and stop rotation timer
|
||||||
|
|
||||||
## 🔧 Configuration
|
## 🔧 Configuration
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,18 +53,28 @@ NOMYO.js implements end-to-end encryption for OpenAI-compatible chat completions
|
||||||
- Automatic key generation on first use
|
- Automatic key generation on first use
|
||||||
- File-based persistence (Node.js)
|
- File-based persistence (Node.js)
|
||||||
- In-memory keys (browsers)
|
- In-memory keys (browsers)
|
||||||
- Password protection via PBKDF2 + AES-256-CBC
|
- Password protection via PBKDF2 + AES-256-CBC (minimum 8-character password enforced)
|
||||||
|
- Automatic periodic key rotation (default: 24 hours, configurable, or disabled with `keyRotationInterval: 0`)
|
||||||
|
- `dispose()` method severs in-memory key references and cancels the rotation timer
|
||||||
|
|
||||||
4. **Transport Security**
|
4. **Transport Security**
|
||||||
- HTTPS enforcement (with warnings for HTTP)
|
- HTTPS enforcement using proper URL parsing (`new URL()`) — not string prefix matching
|
||||||
- Certificate validation (browsers/Node.js)
|
- Certificate validation (browsers/Node.js)
|
||||||
- Optional HTTP for local development (explicit opt-in)
|
- Optional HTTP for local development (explicit opt-in)
|
||||||
|
- API key validated to reject CR/LF characters (prevents HTTP header injection)
|
||||||
|
- Server error detail truncated to 100 printable characters (prevents log injection)
|
||||||
|
|
||||||
5. **Memory Protection (Pure JavaScript)**
|
5. **Memory Protection (Pure JavaScript)**
|
||||||
- Immediate zeroing of sensitive buffers
|
- Immediate zeroing of sensitive buffers
|
||||||
- Context managers for automatic cleanup
|
- Context managers for automatic cleanup (`SecureByteContext`) with guarded `finally` blocks
|
||||||
|
- Intermediate crypto buffers (password bytes, salt, IV) wrapped in `SecureByteContext` during key encryption
|
||||||
|
- HTTP request body (`ArrayBuffer`) zeroed after data is handed to the socket
|
||||||
- Best-effort memory management
|
- Best-effort memory management
|
||||||
|
|
||||||
|
6. **Response Integrity**
|
||||||
|
- Decrypted response validated against required `ChatCompletionResponse` schema fields before use
|
||||||
|
- Generic error messages from all crypto operations (no internal engine details leaked)
|
||||||
|
|
||||||
### ⚠️ Limitations (Pure JavaScript)
|
### ⚠️ Limitations (Pure JavaScript)
|
||||||
|
|
||||||
1. **No OS-Level Memory Locking**
|
1. **No OS-Level Memory Locking**
|
||||||
|
|
@ -94,9 +104,10 @@ NOMYO.js implements end-to-end encryption for OpenAI-compatible chat completions
|
||||||
✅ **DO:**
|
✅ **DO:**
|
||||||
- Use HTTPS in production (enforced by default)
|
- Use HTTPS in production (enforced by default)
|
||||||
- Enable secure memory protection (default: `secureMemory: true`)
|
- Enable secure memory protection (default: `secureMemory: true`)
|
||||||
- Use password-protected private keys in Node.js
|
- Use password-protected private keys in Node.js (minimum 8 characters)
|
||||||
- Set private key file permissions to 600 (owner-only)
|
- Set private key file permissions to 600 (owner-only)
|
||||||
- Rotate keys periodically
|
- Rely on automatic key rotation (`keyRotationInterval`, default 24h) to limit fingerprint lifetime
|
||||||
|
- Call `dispose()` when the client is no longer needed
|
||||||
- Validate server public key fingerprint on first use
|
- Validate server public key fingerprint on first use
|
||||||
|
|
||||||
❌ **DON'T:**
|
❌ **DON'T:**
|
||||||
|
|
@ -248,9 +259,12 @@ class SecureByteContext {
|
||||||
try {
|
try {
|
||||||
return await fn(this.data);
|
return await fn(this.data);
|
||||||
} finally {
|
} finally {
|
||||||
// Always zero, even if exception occurs
|
// Always zero, even if exception occurs.
|
||||||
|
// zeroMemory failure is swallowed so it cannot mask the original error.
|
||||||
if (this.useSecure) {
|
if (this.useSecure) {
|
||||||
new Uint8Array(this.data).fill(0);
|
try {
|
||||||
|
this.secureMemory.zeroMemory(this.data);
|
||||||
|
} catch (_zeroErr) { /* intentional */ }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,6 +342,11 @@ npm install nomyo-native
|
||||||
✅ **Timing Attacks (Partial)**
|
✅ **Timing Attacks (Partial)**
|
||||||
- Web Crypto API uses constant-time operations
|
- Web Crypto API uses constant-time operations
|
||||||
- No length leakage in comparisons
|
- No length leakage in comparisons
|
||||||
|
- Generic error messages from all crypto operations (RSA, AES) — internal engine errors not forwarded
|
||||||
|
|
||||||
|
✅ **Concurrent Key Generation Race**
|
||||||
|
- Promise-chain mutex serialises all `ensureKeys()` callers
|
||||||
|
- No risk of multiple simultaneous key generations overwriting each other
|
||||||
|
|
||||||
✅ **Key Compromise (Forward Secrecy)**
|
✅ **Key Compromise (Forward Secrecy)**
|
||||||
- Ephemeral AES keys
|
- Ephemeral AES keys
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue