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:

  1. Generate a 32-byte random AES session key.
  2. RSA-OAEP-SHA256 encrypt {"username","password","session_key"} with the server public key.
  3. POST the base64 ciphertext.
  4. 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..."
  }
}
Note: All subsequent requests derive a 32-byte AES key from 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.