395 lines
12 KiB
Markdown
395 lines
12 KiB
Markdown
|
|
# NOMYO Secure Java Chat Client
|
||
|
|
|
||
|
|
**OpenAI-compatible secure chat client with end-to-end encryption for NOMYO Inference Endpoints**
|
||
|
|
|
||
|
|
🔒 **All prompts and responses are automatically encrypted and decrypted**
|
||
|
|
|
||
|
|
🔑 **Uses hybrid encryption (AES-256-GCM + RSA-OAEP with 4096-bit keys)**
|
||
|
|
|
||
|
|
🔄 **Drop-in replacement for OpenAI's ChatCompletion API (Java)**
|
||
|
|
|
||
|
|
## 🚀 Quick Start
|
||
|
|
|
||
|
|
### 0. Try It Now (Demo Credentials)
|
||
|
|
|
||
|
|
No account needed — use these public demo credentials to test immediately:
|
||
|
|
|
||
|
|
| | |
|
||
|
|
|---|---|
|
||
|
|
| **API key** | `NOMYO_AI_E2EE_INFERENCE` |
|
||
|
|
| **Model** | `Qwen/Qwen3-0.6B` |
|
||
|
|
|
||
|
|
> **Note:** The demo endpoint uses a fixed 256-token context window and is intended for evaluation only.
|
||
|
|
|
||
|
|
### 1. Installation
|
||
|
|
|
||
|
|
via Maven (recommended):
|
||
|
|
|
||
|
|
```xml
|
||
|
|
<dependency>
|
||
|
|
<groupId>com.nomyo</groupId>
|
||
|
|
<artifactId>nomyo-java</artifactId>
|
||
|
|
<version>1.0.0</version>
|
||
|
|
</dependency>
|
||
|
|
```
|
||
|
|
|
||
|
|
via Gradle:
|
||
|
|
|
||
|
|
```groovy
|
||
|
|
implementation 'com.nomyo:nomyo-java:1.0.0'
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Use the client (same API as OpenAI)
|
||
|
|
|
||
|
|
```java
|
||
|
|
import com.nomyo.client.SecureChatCompletion;
|
||
|
|
import com.nomyo.client.Constants;
|
||
|
|
import java.util.List;
|
||
|
|
import java.util.Map;
|
||
|
|
|
||
|
|
public class Main {
|
||
|
|
public static void main(String[] args) {
|
||
|
|
SecureChatCompletion secureChatCompletion = new SecureChatCompletion(
|
||
|
|
Constants.DEFAULT_BASE_URL,
|
||
|
|
"NOMYO_AI_E2EE_INFERENCE"
|
||
|
|
);
|
||
|
|
|
||
|
|
List<Map<String, Object>> messages = List.of(
|
||
|
|
Map.of("role", "user", "content", "Hello! How are you today?")
|
||
|
|
);
|
||
|
|
|
||
|
|
Map<String, Object> kwargs = Map.of(
|
||
|
|
"security_tier", "standard",
|
||
|
|
"temperature", 0.7
|
||
|
|
);
|
||
|
|
|
||
|
|
var response = secureChatCompletion.create(
|
||
|
|
"Qwen/Qwen3-0.6B",
|
||
|
|
messages,
|
||
|
|
kwargs);
|
||
|
|
|
||
|
|
System.out.println(response.toString());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🔐 Security Features
|
||
|
|
|
||
|
|
### Hybrid Encryption
|
||
|
|
|
||
|
|
- **Payload encryption**: AES-256-GCM (authenticated encryption)
|
||
|
|
- **Key exchange**: RSA-OAEP with SHA-256
|
||
|
|
- **Key size**: 4096-bit RSA keys
|
||
|
|
- **All communication**: End-to-end encrypted
|
||
|
|
|
||
|
|
### Key Management
|
||
|
|
|
||
|
|
- **Automatic key generation**: Keys are automatically generated on first use
|
||
|
|
- **Automatic key loading**: Existing keys are loaded automatically from `client_keys/` directory
|
||
|
|
- **No manual intervention required**: The library handles key management automatically
|
||
|
|
- **Keys kept in memory**: Active session keys are stored in memory for performance
|
||
|
|
- **Optional persistence**: Keys can be saved to `client_keys/` directory for reuse across sessions
|
||
|
|
- **Password protection**: Optional password encryption for private keys (recommended for production)
|
||
|
|
- **Secure permissions**: Private keys stored with restricted permissions (600 - owner-only access)
|
||
|
|
|
||
|
|
### Secure Memory Protection
|
||
|
|
|
||
|
|
### Ephemeral AES Keys
|
||
|
|
|
||
|
|
- **Per-request encryption keys**: A unique AES-256 key is generated for each request
|
||
|
|
- **Automatic rotation**: AES keys are never reused - a fresh key is created for every encryption operation
|
||
|
|
- **Forward secrecy**: Compromise of one AES key only affects that single request
|
||
|
|
- **Secure generation**: AES keys are generated using cryptographically secure random number generation
|
||
|
|
- **Automatic cleanup**: AES keys are zeroed from memory immediately after use
|
||
|
|
- **Automatic protection**: Plaintext payloads are automatically protected during encryption
|
||
|
|
- **Prevents memory swapping**: Sensitive data cannot be swapped to disk
|
||
|
|
- **Guaranteed zeroing**: Memory is zeroed after encryption completes
|
||
|
|
- **Fallback mechanism**: Graceful degradation if SecureMemory module unavailable
|
||
|
|
|
||
|
|
## 🔄 OpenAI Compatibility
|
||
|
|
|
||
|
|
The `SecureChatCompletion` class provides **exact API compatibility** with OpenAI's `ChatCompletion.create()` method.
|
||
|
|
|
||
|
|
### Supported Parameters
|
||
|
|
|
||
|
|
All standard OpenAI parameters are supported:
|
||
|
|
|
||
|
|
- `model`: Model identifier
|
||
|
|
- `messages`: List of message objects (`List<Map<String, Object>>`)
|
||
|
|
- `temperature`: Sampling temperature (0-2)
|
||
|
|
- `max_tokens`: Maximum tokens to generate
|
||
|
|
- `top_p`: Nucleus sampling
|
||
|
|
- `frequency_penalty`: Frequency penalty
|
||
|
|
- `presence_penalty`: Presence penalty
|
||
|
|
- `stop`: Stop sequences
|
||
|
|
- `n`: Number of completions
|
||
|
|
- `tools`: Tool definitions
|
||
|
|
- `tool_choice`: Tool selection strategy
|
||
|
|
- `user`: User identifier
|
||
|
|
- And more...
|
||
|
|
|
||
|
|
### Response Format
|
||
|
|
|
||
|
|
Responses follow the OpenAI format exactly, with an additional `_metadata` field for debugging and security information.
|
||
|
|
|
||
|
|
```java
|
||
|
|
{
|
||
|
|
"id": "chatcmpl-123",
|
||
|
|
"object": "chat.completion",
|
||
|
|
"created": 1234567890,
|
||
|
|
"model": "Qwen/Qwen3-0.6B",
|
||
|
|
"choices": [
|
||
|
|
{
|
||
|
|
"index": 0,
|
||
|
|
"message": {
|
||
|
|
"role": "assistant",
|
||
|
|
"content": "Hello! I'm doing well, thank you for asking.",
|
||
|
|
"tool_calls": [...] // if tools were used
|
||
|
|
},
|
||
|
|
"finish_reason": "stop"
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"usage": {
|
||
|
|
"prompt_tokens": 10,
|
||
|
|
"completion_tokens": 20,
|
||
|
|
"total_tokens": 30
|
||
|
|
},
|
||
|
|
"_metadata": {
|
||
|
|
"payload_id": "openai-compat-abc123", // Unique identifier for this request
|
||
|
|
"processed_at": 1765250382, // Timestamp when server processed the request
|
||
|
|
"is_encrypted": true, // Indicates this response was decrypted
|
||
|
|
"encryption_algorithm": "hybrid-aes256-rsa4096", // Encryption method used
|
||
|
|
"response_status": "success" // Status of the decryption/processing
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
The `_metadata` field contains security-related information about the encrypted communication and is automatically added to all responses.
|
||
|
|
|
||
|
|
## 🛠️ Usage Examples
|
||
|
|
|
||
|
|
### Basic Chat
|
||
|
|
|
||
|
|
```java
|
||
|
|
import com.nomyo.client.SecureChatCompletion;
|
||
|
|
import com.nomyo.client.Constants;
|
||
|
|
import java.util.List;
|
||
|
|
import java.util.Map;
|
||
|
|
|
||
|
|
public class Main {
|
||
|
|
public static void main(String[] args) {
|
||
|
|
SecureChatCompletion client = new SecureChatCompletion(
|
||
|
|
Constants.DEFAULT_BASE_URL,
|
||
|
|
"NOMYO_AI_E2EE_INFERENCE"
|
||
|
|
);
|
||
|
|
|
||
|
|
List<Map<String, Object>> messages = List.of(
|
||
|
|
Map.of("role", "system", "content", "You are a helpful assistant."),
|
||
|
|
Map.of("role", "user", "content", "What is the capital of France?")
|
||
|
|
);
|
||
|
|
|
||
|
|
Map<String, Object> kwargs = Map.of("security_tier", "standard", "temperature", 0.7);
|
||
|
|
|
||
|
|
var response = client.create(
|
||
|
|
"Qwen/Qwen3-0.6B",
|
||
|
|
messages,
|
||
|
|
kwargs
|
||
|
|
);
|
||
|
|
|
||
|
|
// Extract content safely
|
||
|
|
System.out.println(response.get("choices").get(0).get("message").get("content"));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### With Tools
|
||
|
|
|
||
|
|
```java
|
||
|
|
import com.nomyo.client.SecureChatCompletion;
|
||
|
|
import com.nomyo.client.Constants;
|
||
|
|
import java.util.List;
|
||
|
|
import java.util.Map;
|
||
|
|
|
||
|
|
public class Main {
|
||
|
|
public static void main(String[] args) {
|
||
|
|
SecureChatCompletion client = new SecureChatCompletion(
|
||
|
|
Constants.DEFAULT_BASE_URL,
|
||
|
|
"NOMYO_AI_E2EE_INFERENCE"
|
||
|
|
);
|
||
|
|
|
||
|
|
List<Map<String, Object>> messages = List.of(
|
||
|
|
Map.of("role", "user", "content", "What's the weather in Paris?")
|
||
|
|
);
|
||
|
|
|
||
|
|
Map<String, Object> tools = Map.of(
|
||
|
|
"type", "function",
|
||
|
|
"function", Map.of(
|
||
|
|
"name", "get_weather",
|
||
|
|
"description", "Get weather information",
|
||
|
|
"parameters", Map.of(
|
||
|
|
"type", "object",
|
||
|
|
"properties", Map.of(
|
||
|
|
"location", Map.of("type", "string")
|
||
|
|
),
|
||
|
|
"required", List.of("location")
|
||
|
|
)
|
||
|
|
)
|
||
|
|
);
|
||
|
|
|
||
|
|
Map<String, Object> kwargs = Map.of("security_tier", "standard", "temperature", 0.7);
|
||
|
|
|
||
|
|
var response = client.create(
|
||
|
|
"Qwen/Qwen3-0.6B",
|
||
|
|
messages,
|
||
|
|
kwargs
|
||
|
|
);
|
||
|
|
|
||
|
|
System.out.println(response.get("choices").get(0).get("message").get("content"));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 📦 Dependencies
|
||
|
|
|
||
|
|
- **Maven Coordinates**: `com.nomyo:nomyo-java`
|
||
|
|
- **Java Version**: Java 11+ (Required for HTTP Client and crypto primitives)
|
||
|
|
- **JSON Processing**: Jackson Databind (for Map/JSON handling)
|
||
|
|
- **HTTP Client**: Apache HttpClient or Java NIO (Included in core)
|
||
|
|
- **Crypto**: BouncyCastle or JDK Crypto (Included in core)
|
||
|
|
|
||
|
|
## 🔧 Configuration
|
||
|
|
|
||
|
|
### Custom Base URL
|
||
|
|
|
||
|
|
```java
|
||
|
|
SecureChatCompletion client = new SecureChatCompletion("https://NOMYO-Pro-Router:12434", "YOUR_API_KEY");
|
||
|
|
```
|
||
|
|
|
||
|
|
### API Key Authentication
|
||
|
|
|
||
|
|
```java
|
||
|
|
// Initialize with API key (recommended for production)
|
||
|
|
SecureChatCompletion client = new SecureChatCompletion(
|
||
|
|
"https://api.nomyo.ai",
|
||
|
|
"your-api-key-here"
|
||
|
|
);
|
||
|
|
|
||
|
|
// Or pass API key in the create() method if supported by extension
|
||
|
|
// Map<String, Object> kwargs = Map.of("security_tier", "standard", "api_key", "your-api-key-here");
|
||
|
|
```
|
||
|
|
|
||
|
|
### Secure Memory Configuration
|
||
|
|
|
||
|
|
The library enables secure memory protection by default.
|
||
|
|
|
||
|
|
```java
|
||
|
|
// Enable secure memory protection (default)
|
||
|
|
SecureChatCompletion client = new SecureChatCompletion("https://api.nomyo.ai", "YOUR_API_KEY");
|
||
|
|
|
||
|
|
// Disable secure memory (not recommended, for testing only)
|
||
|
|
// Requires passing specific config flag if available in version
|
||
|
|
SecureChatCompletion client = new SecureChatCompletion("https://api.nomyo.ai", "YOUR_API_KEY");
|
||
|
|
```
|
||
|
|
|
||
|
|
### Key Management
|
||
|
|
|
||
|
|
Keys are automatically generated on first use.
|
||
|
|
|
||
|
|
#### Generate Keys Manually
|
||
|
|
|
||
|
|
```java
|
||
|
|
import com.nomyo.client.SecureCompletionClient;
|
||
|
|
|
||
|
|
public class KeyManager {
|
||
|
|
public static void main(String[] args) {
|
||
|
|
SecureCompletionClient client = new SecureCompletionClient();
|
||
|
|
client.generateKeys(true, "client_keys", "your-password");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Load Existing Keys
|
||
|
|
|
||
|
|
```java
|
||
|
|
import com.nomyo.client.SecureCompletionClient;
|
||
|
|
|
||
|
|
public class KeyManager {
|
||
|
|
public static void main(String[] args) {
|
||
|
|
SecureCompletionClient client = new SecureCompletionClient();
|
||
|
|
client.loadKeys("client_keys/private_key.pem", "client_keys/public_key.pem", "your-password");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## 📚 API Reference
|
||
|
|
|
||
|
|
### SecureChatCompletion
|
||
|
|
|
||
|
|
#### Constructor
|
||
|
|
|
||
|
|
```java
|
||
|
|
SecureChatCompletion(
|
||
|
|
String base_url,
|
||
|
|
String api_key
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
**Parameters:**
|
||
|
|
|
||
|
|
- `base_url`: Base URL of the NOMYO Router (must use HTTPS for production)
|
||
|
|
- `api_key`: Optional API key for bearer authentication
|
||
|
|
|
||
|
|
#### Methods
|
||
|
|
|
||
|
|
- `create(String model, List<Map<String, Object>> messages, Map<String, Object> kwargs)`: Create a chat completion
|
||
|
|
|
||
|
|
### SecureCompletionClient
|
||
|
|
|
||
|
|
#### Constructor
|
||
|
|
|
||
|
|
```java
|
||
|
|
SecureCompletionClient(
|
||
|
|
String router_url
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Methods
|
||
|
|
|
||
|
|
- `generateKeys(boolean saveToFile, String keyDir, String password)`: Generate RSA key pair
|
||
|
|
- `loadKeys(String private_key_path, String public_key_path, String password)`: Load keys from files
|
||
|
|
- `fetchServerPublicKey()`: Fetch server's public key
|
||
|
|
- `encryptPayload(Map<String, Object> payload)`: Encrypt a payload
|
||
|
|
- `decryptResponse(Map<String, Object> encrypted_response, String payload_id)`: Decrypt a response
|
||
|
|
- `sendSecureRequest(Map<String, Object> payload, String payload_id)`: Send encrypted request and receive decrypted response
|
||
|
|
|
||
|
|
## 📝 Notes
|
||
|
|
|
||
|
|
### Security Best Practices
|
||
|
|
|
||
|
|
- Always use password protection for private keys in production
|
||
|
|
- Keep private keys secure (permissions set to 600)
|
||
|
|
- Never share your private key
|
||
|
|
- Verify server's public key fingerprint before first use
|
||
|
|
|
||
|
|
### Performance
|
||
|
|
|
||
|
|
- Key generation takes ~1-2 seconds (one-time operation)
|
||
|
|
- Encryption/decryption adds minimal overhead (~10-20ms per request)
|
||
|
|
|
||
|
|
### Compatibility
|
||
|
|
|
||
|
|
- Works with any OpenAI-compatible code
|
||
|
|
- No changes needed to existing OpenAI client code
|
||
|
|
- Simply replace `openai.ChatCompletion.create()` with `SecureChatCompletion.create()`
|
||
|
|
|
||
|
|
## 🤝 Contributing
|
||
|
|
|
||
|
|
Contributions are welcome! Please open issues or pull requests on the project repository.
|
||
|
|
|
||
|
|
## 📄 License
|
||
|
|
|
||
|
|
See LICENSE file for licensing information.
|
||
|
|
|
||
|
|
## 📞 Support
|
||
|
|
|
||
|
|
For questions or issues, please refer to the project documentation or open an issue.
|