Skip to content

Architecture

The bilbycast-appear-x-api-gateway acts as a protocol bridge between two systems:

┌──────────────────┐ WebSocket (wss://) ┌──────────────────────────────┐ JSON-RPC 2.0 (HTTPS) ┌──────────────────┐
│ │◄──────────────────────────►│ bilbycast-appear-x-api-gw │◄──────────────────────────►│ │
│ bilbycast-manager│ stats, health, commands │ │ GetInputs, SetOutputs, │ Appear X Unit │
│ │ command_ack │ ┌────────┐ ┌───────────┐ │ GetActiveAlarms, ... │ (Chassis) │
│ Dashboard, AI, │ │ │Polling │ │ Command │ │ │ │
│ Topology │ │ │Engine │ │ Handler │ │ │ Slot 1: IP GW │
│ │ │ └────────┘ └───────────┘ │ │ Slot 2: Encoder │
└──────────────────┘ │ │ │ │ │ Slot N: ... │
│ └─────┬─────┘ │ └──────────────────┘
│ │ │
│ ┌──────┴──────┐ │
│ │ WS Client │ │
│ └─────────────┘ │
└──────────────────────────────┘
  1. Polling engine calls Appear X JSON-RPC methods on configured intervals
  2. Responses are mapped to stats or health messages
  3. Messages sent through an mpsc channel to the WebSocket client
  4. WS client wraps in WsEnvelope and sends to manager
  5. Manager’s NodeHub receives the message, updates cached stats, broadcasts to browser dashboards
  6. The AppearXDriver in the manager extracts metrics for display
  1. User clicks an action button in the AI assistant (or sends a command via API)
  2. Manager sends a command message via WebSocket to the gateway
  3. The SDK read loop invokes CommandHandler::handle_command directly on the received command (no local mpsc + oneshot hop, no separate handler task)
  4. The handler translates the action type to an Appear X JSON-RPC method
  5. JSON-RPC call made to the Appear X unit
  6. The SDK packs the handler’s return value into a command_ack and sends it to the manager
  7. Manager forwards the result to the UI

The gateway derives health status from Appear X alarms:

Alarm Severity Health Status
MAJOR or CRITICAL present critical
MINOR or WARNING present degraded
No alarms ok

The gateway implements the exact same security model as bilbycast-edge and bilbycast-relay:

  1. TLS enforcement: Only wss:// connections accepted
  2. Three TLS modes:
    • Standard: Validates against system CA roots (webpki-roots)
    • Self-signed: Bypasses all cert validation (requires BILBYCAST_ALLOW_INSECURE=1)
    • Pinned: SHA-256 fingerprint verification of the server certificate
  3. Auth as first frame: Credentials sent in the first WebSocket message, not in URL/headers
  4. Credential persistence: After registration, node_id + node_secret saved to a file with 0600 permissions
  5. Reconnect backoff: steps through 1, 2, 5, 10, 30 seconds on connection failures, reset on a successful auth
  • HTTPS with optional self-signed cert acceptance (separate from manager TLS)
  • Bearer token authentication via JSON-RPC BeginSession
  • Token auto-refresh on expiry (re-authenticates and retries the failed call)

Two top-level tasks run concurrently:

main()
├── spawn: polling engine (multiple sub-tasks per board/poll type)
└── await: SDK GatewayClient (blocks until cancellation; its read loop dispatches commands inline)

There is no separate spawned command-handler task — the SDK’s GatewayClient read loop dispatches each command inline. Communication uses the SDK’s channels:

  • An Emitter (backed by a single bounded OutboundFrame channel) — polling → SDK client (stats/health/event messages)
  • A CommandHandler trait — the SDK read loop invokes handle_command directly and automatically packs the return value into a command_ack

Graceful shutdown uses tokio_util::CancellationToken tree — cancelling the root token propagates to all child tokens.

Type URL Pattern Used For
MMI https://{addr}/mmi/api/jsonrpc Alarms, chassis model, authentication
Board https://{addr}/board/{slot_hex}/api/jsonrpc IP gateway, encoder, ASI per slot
Service https://{addr}/mmi/service_{name}/api/jsonrpc Cross-board services
<interface>:<version>/<module>/<command>

Examples:

  • mmi:2.16/alarms/GetActiveAlarms
  • ipGateway:1.15/input/GetInputs
  • ipGateway:1.15/output/SetOutputs
  • board:2.16/services/GetInputServices

The Appear X API uses identical data structures for Get and Set operations. This means you can:

  1. Call GetInputs to fetch current configuration
  2. Modify the desired fields in the response
  3. Call SetInputs with the modified data

The gateway’s command handler leverages this: set_ip_input passes the inputs array directly to SetInputs.

All entities in the Appear X platform are addressed by UUID:

  • IP interfaces have UUIDs (referenced by inputs/outputs)
  • Inputs have UUIDs (published as sources)
  • Services within inputs have child UUIDs
  • Outputs reference source UUIDs for content mapping

The AI assistant in the manager understands this reference system and can help users configure the correct UUID mappings.