- Changed pytest calls to use `venv/bin/python -m pytest` with explicit config - Added `--rootdir "$PWD"` and `--import-mode=importlib` for consistent imports - Fixed PYTHONPATH to use absolute paths with $PWD prefix - Added smart contract security scanning for Solidity files - Added Circom circuit security checks for ZK proof circuits - Added ZK proof implementation security validation - Added contracts/** to security scanning workflow
9.9 KiB
WebSocket API Documentation
The AITBC platform provides WebSocket endpoints for real-time updates on job status, blockchain events, and marketplace activities.
Overview
WebSocket connections provide real-time, bidirectional communication with the AITBC services. This is particularly useful for:
- Monitoring job status changes
- Receiving blockchain event notifications
- Tracking marketplace offers and transactions
- Real-time system health monitoring
Connection URLs
Coordinator API WebSocket
- Development:
ws://localhost:8011/v1/jobs/{job_id}/ws - Production:
wss://aitbc.bubuit.net/api/v1/jobs/{job_id}/ws
Blockchain API WebSocket
- Development:
ws://localhost:8080/v1/events - Production:
wss://aitbc.bubuit.net/api/v1/events
Marketplace WebSocket
- Development:
ws://localhost:8102/v1/events - Production:
wss://aitbc.bubuit.net/api/v1/events
Authentication
WebSocket connections require authentication via query parameters:
ws://localhost:8011/v1/jobs/{job_id}/ws?api_key=your-api-key
Alternatively, use the X-Api-Key header during the WebSocket handshake.
Job Status WebSocket
Endpoint
ws://localhost:8011/v1/jobs/{job_id}/ws
Message Format
Status updates are sent as JSON messages:
{
"job_id": "abc123",
"state": "RUNNING",
"assigned_miner_id": "miner-456",
"timestamp": "2026-05-11T10:00:00Z",
"progress": 0.5
}
States
QUEUED- Job waiting for miner assignmentRUNNING- Job currently processingCOMPLETED- Job finished successfullyFAILED- Job failed with errorCANCELLED- Job cancelled by userEXPIRED- Job exceeded TTL
Example (Python)
import asyncio
import websockets
import json
async def monitor_job(job_id: str, api_key: str):
uri = f"ws://localhost:8011/v1/jobs/{job_id}/ws?api_key={api_key}"
async with websockets.connect(uri) as websocket:
async for message in websocket:
data = json.loads(message)
print(f"State: {data['state']}")
print(f"Progress: {data.get('progress', 0)}")
if data['state'] in ['COMPLETED', 'FAILED', 'CANCELLED', 'EXPIRED']:
break
asyncio.run(monitor_job("job-id", "your-api-key"))
Example (JavaScript)
const ws = new WebSocket('ws://localhost:8011/v1/jobs/job-id/ws?api_key=your-api-key');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log(`State: ${data.state}`);
console.log(`Progress: ${data.progress || 0}`);
if (['COMPLETED', 'FAILED', 'CANCELLED', 'EXPIRED'].includes(data.state)) {
ws.close();
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
ws.onclose = () => {
console.log('WebSocket connection closed');
};
Example (cURL with websocat)
websocat ws://localhost:8011/v1/jobs/job-id/ws?api_key=your-api-key
Blockchain Events WebSocket
Endpoint
ws://localhost:8080/v1/events
Message Format
Blockchain events are sent as JSON messages:
{
"type": "new_block",
"block": {
"height": 12346,
"hash": "0x...",
"timestamp": "2026-05-11T10:00:00Z",
"transactions": []
}
}
Event Types
new_block- New block minedtransaction_confirmed- Transaction confirmedtransaction_pending- Transaction submitted to mempoolfork_detected- Blockchain fork detectedsync_status- Node sync status update
Example (Python)
import asyncio
import websockets
import json
async def monitor_blockchain():
uri = "ws://localhost:8080/v1/events"
async with websockets.connect(uri) as websocket:
async for message in websocket:
data = json.loads(message)
if data['type'] == 'new_block':
print(f"New block: {data['block']['height']}")
elif data['type'] == 'transaction_confirmed':
print(f"Transaction confirmed: {data['tx_hash']}")
asyncio.run(monitor_blockchain())
Example (JavaScript)
const ws = new WebSocket('ws://localhost:8080/v1/events');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'new_block':
console.log(`New block: ${data.block.height}`);
break;
case 'transaction_confirmed':
console.log(`Transaction confirmed: ${data.tx_hash}`);
break;
default:
console.log(`Unknown event type: ${data.type}`);
}
};
Marketplace WebSocket
Endpoint
ws://localhost:8102/v1/events
Message Format
Marketplace events are sent as JSON messages:
{
"type": "new_offer",
"offer": {
"id": "offer-123",
"gpu_type": "nvidia-rtx-3090",
"gpu_memory": 24,
"price_per_hour": 0.5,
"currency": "AITBC"
}
}
Event Types
new_offer- New GPU offer postedoffer_matched- Offer matched with joboffer_expired- Offer expiredoffer_cancelled- Offer cancelled by providerprice_update- Offer price updated
Example (Python)
import asyncio
import websockets
import json
async def monitor_marketplace():
uri = "ws://localhost:8102/v1/events"
async with websockets.connect(uri) as websocket:
async for message in websocket:
data = json.loads(message)
if data['type'] == 'new_offer':
offer = data['offer']
print(f"New offer: {offer['gpu_type']}, {offer['gpu_memory']}GB, ${offer['price_per_hour']}/hr")
asyncio.run(monitor_marketplace())
Connection Management
Heartbeat
WebSocket connections should send periodic heartbeat messages to keep the connection alive:
import asyncio
import websockets
async def send_heartbeat(websocket, interval=30):
while True:
try:
await websocket.send(json.dumps({"type": "ping"}))
await asyncio.sleep(interval)
except:
break
Reconnection Logic
Implement automatic reconnection with exponential backoff:
import asyncio
import websockets
async def connect_with_retry(uri, max_retries=5):
retry_delay = 1
for attempt in range(max_retries):
try:
async with websockets.connect(uri) as websocket:
return websocket
except:
if attempt < max_retries - 1:
await asyncio.sleep(retry_delay)
retry_delay *= 2
else:
raise
Error Handling
Handle common WebSocket errors:
async def handle_websocket_errors(websocket):
try:
async for message in websocket:
# Process message
pass
except websockets.exceptions.ConnectionClosed:
print("Connection closed")
except websockets.exceptions.WebSocketException as e:
print(f"WebSocket error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
Security Considerations
Use WSS in Production
Always use secure WebSocket connections (wss://) in production:
// Production
const ws = new WebSocket('wss://aitbc.bubuit.net/api/v1/jobs/job-id/ws');
// Development only
const ws = new WebSocket('ws://localhost:8011/v1/jobs/job-id/ws');
API Key Protection
- Never expose API keys in client-side code
- Use environment variables or secure token storage
- Rotate API keys regularly
- Implement rate limiting on WebSocket connections
Origin Validation
The server validates the Origin header to prevent CSRF attacks. Ensure your client sends the correct origin:
const ws = new WebSocket('ws://localhost:8011/v1/jobs/job-id/ws', [], {
headers: {
'Origin': 'https://your-domain.com'
}
});
Rate Limiting
WebSocket connections are rate limited:
- Maximum connections per IP: 10
- Maximum messages per second: 100
- Connection duration limit: 24 hours
Exceeding limits will result in connection termination.
Testing
Python Testing
import pytest
import asyncio
import websockets
@pytest.mark.asyncio
async def test_job_websocket():
uri = "ws://localhost:8011/v1/jobs/test-job/ws?api_key=test-key"
async with websockets.connect(uri) as websocket:
message = await websocket.recv()
data = json.loads(message)
assert 'state' in data
JavaScript Testing
import { describe, it, expect, vi } from 'vitest';
describe('WebSocket', () => {
it('should connect to job WebSocket', async () => {
const WebSocket = vi.fn();
WebSocket.mockReturnValueOnce({
onmessage: vi.fn(),
onerror: vi.fn(),
onclose: vi.fn()
});
const ws = new WebSocket('ws://localhost:8011/v1/jobs/test/ws');
expect(WebSocket).toHaveBeenCalledWith('ws://localhost:8011/v1/jobs/test/ws');
});
});
Troubleshooting
Connection Refused
- Check if the service is running
- Verify the URL and port are correct
- Check firewall rules
Authentication Failed
- Verify API key is valid
- Check API key is passed correctly (query parameter or header)
- Ensure API key has required permissions
Connection Drops
- Check network stability
- Implement reconnection logic
- Verify server logs for errors
No Messages Received
- Verify event subscription
- Check if events are being generated
- Monitor server logs
Best Practices
- Always implement reconnection logic - Network connections can be unstable
- Use exponential backoff - Don't flood the server with reconnection attempts
- Clean up connections - Close WebSocket connections when no longer needed
- Handle errors gracefully - Provide user feedback for connection issues
- Rate limit client-side - Don't send messages faster than the server can handle
- Use message queuing - Buffer messages if the connection is temporarily unavailable
- Monitor connection health - Implement heartbeat/ping-pong mechanism
- Log connection events - Track connection lifecycle for debugging