Edge Agent WebSocket Configuration¶
Last Verified: February 2026
Summary¶
The backend exposes a WebSocket endpoint for the Mac mini edge relay to receive commands in real time (reflex bubbles, scheduling, context updates).
The reference edge implementation lives in ../archety-edge.
WebSocket Connection Details¶
Endpoint¶
Production (canonical): wss://api.ikiro.ai/edge/ws?edge_agent_id=...
Legacy Railway URLs may still work: wss://archety-backend-prod.up.railway.app/edge/ws?...
Required Headers¶
Where {EDGE_TOKEN} is either:
1. Recommended (production): a Base64 HMAC token (see below)
2. Legacy fallback (development only): the raw EDGE_SECRET value (backend rejects this when ENVIRONMENT=production)
Example Connection (JavaScript/Node.js)¶
const WebSocket = require('ws');
// EDGE_TOKEN can be the raw EDGE_SECRET (dev only) OR a generated HMAC token (recommended)
const EDGE_TOKEN = process.env.EDGE_TOKEN || process.env.EDGE_SECRET;
const EDGE_AGENT_ID = 'edge_13106781670'; // Your edge agent ID
const ws = new WebSocket(
`wss://api.ikiro.ai/edge/ws?edge_agent_id=${EDGE_AGENT_ID}`,
{
headers: {
'Authorization': `Bearer ${EDGE_TOKEN}`,
'X-Edge-Agent-Id': EDGE_AGENT_ID
}
}
);
ws.on('open', function open() {
console.log('✅ WebSocket connected - real-time mode enabled');
// Send ping every 30 seconds to keep connection alive
setInterval(() => {
ws.send(JSON.stringify({ type: 'ping' }));
}, 30000);
});
ws.on('message', function message(data) {
const msg = JSON.parse(data.toString());
switch (msg.type) {
case 'command':
handleCommand(msg.data);
sendCommandAck(msg.data.command_id);
break;
case 'pong':
// Server acknowledged our ping
break;
case 'config_update':
handleConfigUpdate(msg.data);
break;
}
});
ws.on('error', function error(err) {
console.error('WebSocket error:', err);
});
ws.on('close', function close(code, reason) {
console.log(`WebSocket closed: ${code} - ${reason}`);
// Implement reconnection with exponential backoff
setTimeout(connect, getBackoffDelay());
});
Message Protocol¶
1. Commands (Backend → Edge)¶
{
"type": "command",
"data": {
"command_id": "cmd_xyz",
"command_type": "send_message_now",
"payload": {
"thread_id": "iMessage;-;+13105551234",
"text": "okok gimme 2 sec",
"bubble_type": "reflex"
},
"priority": "immediate",
"timestamp": "2026-02-16T00:00:00Z"
}
}
2. Command Acknowledgment (Edge → Backend)¶
{
"type": "command_ack",
"data": {
"command_id": "cmd_xyz",
"status": "completed",
"completed_at": "2025-11-05T20:00:01Z",
"error": null
}
}
3. Ping/Pong (Keepalive)¶
Edge sends:
Backend responds:
Reconnection Strategy¶
Implement exponential backoff:
let reconnectDelay = 1000; // Start with 1 second
const maxDelay = 60000; // Max 60 seconds
function getBackoffDelay() {
const delay = reconnectDelay;
reconnectDelay = Math.min(reconnectDelay * 2, maxDelay);
return delay;
}
function connect() {
// Create new WebSocket connection
// Reset delay on successful connection
ws.on('open', () => {
reconnectDelay = 1000;
});
}
Authentication Notes¶
Recommended (HMAC token)¶
For production, use a signed token:
const crypto = require('crypto');
function generateHMACToken(edge_agent_id, user_phone) {
const timestamp = Math.floor(Date.now() / 1000);
const tokenData = `${edge_agent_id}:${user_phone}:${timestamp}`;
const signature = crypto
.createHmac('sha256', EDGE_SECRET)
.update(tokenData)
.digest('hex');
const token = `${tokenData}:${signature}`;
return Buffer.from(token).toString('base64');
}
Send the generated token as Authorization: Bearer <token>.
Testing the Connection¶
-
Check WebSocket endpoint is accessible:
-
Monitor connection in logs: Look for these log messages:
"Using edge_agent_id from query params: edge_13106781670""Authenticated with simple secret (fallback mode)"-
"WebSocket connection established for edge_13106781670" -
Test command delivery: Commands sent to your edge agent will arrive instantly via WebSocket instead of waiting for HTTP polling.
Benefits¶
- Instant command delivery (<100ms vs up to 15s with HTTP polling)
- Reduced HTTP requests (99% reduction in polling traffic)
- Real-time bidirectional communication
- Automatic fallback to HTTP polling if WebSocket disconnects
Troubleshooting¶
403 Forbidden Error¶
- Verify
EDGE_SECRETmatches between edge client and backend - Check headers are properly set
- Ensure
edge_agent_idis included in query params
Connection Drops¶
- Implement ping/pong keepalive (every 30 seconds)
- Use exponential backoff for reconnection
- Monitor network stability
Commands Not Received¶
- Verify WebSocket is connected (check logs)
- Ensure command acknowledgments are being sent
- Check for any error messages in WebSocket
messageevents
Environment Variables¶
Ensure your edge client has:
EDGE_SECRET=<shared_secret> # shared signing secret (used to generate EDGE_TOKEN)
EDGE_TOKEN=<auth_token_to_send> # raw EDGE_SECRET (dev only) OR generated HMAC token
EDGE_AGENT_ID=edge_13106781670 # Your specific edge agent ID
BACKEND_WS_URL=wss://api.ikiro.ai/edge/ws
Support¶
For issues or questions: 1. Check Railway deployment logs for backend status 2. Review edge agent logs for connection attempts 3. Verify network connectivity and firewall rules 4. Check the WebSocket statistics endpoint (requires admin key):
Backward Compatibility: HTTP polling via POST /edge/sync remains available when WebSocket is down.