WebSocket API
The WebSocket API provides real-time communication between the Secrets Agent and the Mobile App for secret approval requests.
Connection
| Environment | URL | Notes |
|---|---|---|
| Local dev | ws://localhost:8765 | Default port, configurable |
| Production | wss://host:port | TLS required |
Authentication Handshake
The WebSocket connection uses a challenge-response authentication flow with Ed25519 signatures:
- Client connects to the WebSocket server
- Server sends a
challengemessage with a 32-byte CSPRNG nonce - Client signs the nonce with its Ed25519 private key
- Client sends an
authmessage with the signature and public key - Server verifies the signature and responds with
auth_okorauth_error
Handshake sequence text
Client Server
| |
| ←── challenge { nonce } |
| |
| ──→ auth { sig, pubkey } |
| |
| ←── auth_ok / auth_error |
| | Server → Client Messages
challenge
Sent immediately after connection. Contains a one-time nonce for authentication.
| Field | Type | Description |
|---|---|---|
type | string | "challenge" |
nonce | string (hex) | 32-byte CSPRNG nonce, hex-encoded |
timestamp | integer | Unix timestamp (seconds) |
auth_ok
Authentication succeeded. Connection is now authorized.
| Field | Type | Description |
|---|---|---|
type | string | "auth_ok" |
session_id | string | Unique session identifier |
auth_error
Authentication failed. Connection will be closed.
| Field | Type | Description |
|---|---|---|
type | string | "auth_error" |
reason | string | Error description |
secret_request
A secret access request pending user approval.
| Field | Type | Description |
|---|---|---|
type | string | "secret_request" |
request_id | string | Unique request identifier |
secret_name | string | Name of the requested secret |
secret_type | string | Type (PASSWORD, API_KEY, etc.) |
tool_name | string | Tool requesting access |
reason | string | Context for the request |
duration | string | Requested access duration |
ping
Keepalive ping. Client must respond with pong.
| Field | Type | Description |
|---|---|---|
type | string | "ping" |
timestamp | integer | Unix timestamp |
Client → Server Messages
auth
Response to the challenge message.
| Field | Type | Description |
|---|---|---|
type | string | "auth" |
signature | string (hex) | Ed25519 signature of the nonce |
public_key | string (hex) | Client's Ed25519 public key |
secret_response
User's response to a secret access request.
| Field | Type | Description |
|---|---|---|
type | string | "secret_response" |
request_id | string | Matching request identifier |
approved | boolean | true to approve, false to deny |
pong
Keepalive response. Must be sent in reply to ping.
| Field | Type | Description |
|---|---|---|
type | string | "pong" |
Security
- Nonce consuming: Each challenge nonce can only be used once. Replay attacks are prevented.
- Timestamp drift: Nonces older than 30 seconds are rejected.
- Ed25519 signatures: 32-byte keys, 64-byte signatures. The server verifies the client's public key is registered.
- TLS mandatory: In production, connections must use
wss://for transport encryption. - E2E encryption: Secret values are encrypted with X25519 ECDH + AES-256-GCM between mobile and secrets agent. The WebSocket transport never sees plaintext.
JavaScript Client Example
websocket-client.js javascript
const ws = new WebSocket('ws://localhost:8765');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'challenge':
// Sign the nonce with Ed25519 private key
const signature = ed25519Sign(msg.nonce, privateKey);
ws.send(JSON.stringify({
type: 'auth',
signature: signature,
public_key: publicKeyHex
}));
break;
case 'auth_ok':
console.log('Authenticated:', msg.session_id);
break;
case 'secret_request':
// Show approval UI to user
showApprovalDialog(msg);
break;
case 'ping':
ws.send(JSON.stringify({ type: 'pong' }));
break;
}
};