API Usage Guide
The RIVO platform uses end-to-end AES-GCM encryption on all authenticated endpoints. All protected
requests must carry an Authorization Bearer token and an encrypted JSON body.
6.1 Authentication
Fetch Public Key
Before login, retrieve the server's RSA public key:
GET /api/v1/auth/key
Response:
{
"success": true,
"data": { "public_key": "-----BEGIN PUBLIC KEY-----\n..." }
}
Login (E2E Encrypted)
RIVO uses a hybrid RSA-OAEP + AES-GCM login handshake:
- Generate a 32-byte random AES session key.
- RSA-OAEP-SHA256 encrypt
{"username","password","session_key"}with the server public key. - POST the base64 ciphertext.
- Decrypt the response with the AES session key to get tokens.
POST /api/v1/auth/login
Content-Type: application/json
{
"encrypted_data": "<base64(RSA-OAEP(payload))>"
}
Decrypted response payload:
{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"certification_key": "ck_...",
"account_id": "acc_...",
"expires_at": "2026-01-01T00:00:00Z"
}
Refresh Token
Use a plain JSON request (no encryption) to refresh:
POST /api/v1/auth/refresh
Content-Type: application/json
{ "refresh_token": "eyJ..." }
Response:
{
"success": true,
"data": {
"access_token": "eyJ...",
"refresh_token": "eyJ..."
}
}
SHA-256(access_token) and wrap the body as
{"encrypted_data":"<base64(nonce || ciphertext+tag)>"}.
The RIVO Node SDK handles this transparently.
6.2 Robot Management
Register a Robot
Register the node to your fleet. Safe to call on every startup — returns 409 if already registered.
POST /api/v1/robots
Authorization: Bearer <access_token>
{
"robot_id": "turtlebot-001",
"robot_type": "turtlebot3",
"robot_name": "Warehouse Bot #1",
"group_id": "warehouse-a" // optional
}
Response 200: { "success": true }
Response 409: robot already registered (safe to ignore)
6.3 Status & Telemetry
Update Robot Status
Heartbeat and status flush (default interval: 2 s from RIVO Node).
POST /api/v1/robots/{robotId}/status
Authorization: Bearer <access_token>
// Encrypted body (AES-GCM). Plaintext payload example:
{
"status": {
"battery_level": 85,
"cpu_usage": 12.4,
"state": "active"
}
}
Post Telemetry
Push sensor topic readings (default interval: 500 ms from RIVO Node).
POST /api/v1/robots/{robotId}/telemetry
Authorization: Bearer <access_token>
// Plaintext payload example:
{
"timestamp": "2026-01-01T00:00:00Z",
"topics": {
"temperature": { "value": 36.5, "unit": "°C" },
"humidity": { "value": 62.1, "unit": "%" }
}
}
6.4 Alerts
Submit an alert from a robot node. Alerts are sent immediately, not batched.
POST /api/v1/robots/{robotId}/alert
Authorization: Bearer <access_token>
{
"alert_type": "battery_low",
"severity": "warning", // info | warning | error
"message": "Battery at 10%",
"action": "return_to_base",
"timestamp": "2026-01-01T00:00:00Z"
}
6.5 Camera Streaming
The RIVO Node publishes VP8 camera streams via LiveKit WebRTC. The node obtains a room token automatically on startup.
Get Streaming Token
GET /api/v1/robots/{robotId}/token?identity=camera
Authorization: Bearer <access_token>
// Decrypted response payload:
{
"token": "<LiveKit JWT>",
"host": "wss://livekit.example.com",
"room": "turtlebot-001"
}
The RIVO Node then connects to LiveKit automatically and publishes a TrackLocalStaticRTP
VP8 track using the GStreamer pipeline:
gst-launch-1.0 v4l2src device=/dev/video0 \
! image/jpeg,width=1280,height=720,framerate=30/1 \
! jpegdec \
! vp8enc error-resilient=partitions keyframe-max-dist=10 \
auto-alt-ref=true cpu-used=5 deadline=1 \
! rtpvp8pay mtu=1100 \
! udpsink host=127.0.0.1 port=<auto>
A custom GStreamer pipeline can be set via cameras[*].gst_pipeline in
config.yaml.
6.6 Commands & Control
Send commands to robots via an encrypted WebSocket channel. The node subscribes to the channel on startup and acknowledges every command.
WebSocket Command Channel
WS /api/v1/robots/{robotId}/commands?token=<access_token>
// Server → Node (AES-GCM encrypted JSON):
{
"id": "cmd-uuid",
"type": "gripper_open",
"parameters": { "force": 10 },
"timestamp": "2026-01-01T00:00:00Z"
}
// Node → Server ACK (AES-GCM encrypted JSON):
{
"command_id": "cmd-uuid",
"status": "completed", // received | in_progress | completed | failed
"message": "Gripper opened"
}
6.7 Node Config Reference (config.yaml)
node:
robot_id: "my-robot-001" # auto-derived from machine-id if omitted
robot_type: "custom"
robot_name: "My RIVO Node"
group_id: ""
server:
url: "http://server:8080"
timeout_seconds: 10
auth:
mode: "access_token" # or "cert_key"
username: "admin@rivo.io" # triggers auto-login
password: "password"
# Or pre-issued tokens:
# access_token: "eyJ..."
# refresh_token: "eyJ..."
cameras:
- name: "main"
enabled: true
device: "/dev/video0"
width: 1280
height: 720
framerate: 30
# gst_pipeline: "" # optional full pipeline override
telemetry:
status_interval: "2s"
telemetry_interval: "500ms"
uarts:
mcu:
enabled: true
port: "/dev/ttyUSB0"
baud: 115200
framing: "cobs" # or slip
encoding: "json" # or cbor
protocol: "bme280"
i2c:
enabled: false
bus: 1
spool:
enabled: false
protocols:
dir: "protocols"
logging:
level: "info"
6.8 E2E Encryption Details
All authenticated API payloads are AES-256-GCM encrypted. The key is derived as:
aes_key = SHA-256(raw_access_token_string) // 32 bytes
Wire format:
encrypted_data = base64( nonce[12 bytes] || ciphertext || GCM-tag[16 bytes] )
JSON wrapper:
{ "encrypted_data": "<base64>" }
The RIVO Node SDK (internal/server/crypto.go) handles key derivation, encryption, and
decryption automatically via EncryptPayload / DecryptPayload.