Rhema
Reference

Remote Control API

Wire-level reference for the OSC addresses and HTTP endpoints that drive Rhema from external controllers.

This page is the protocol-level reference. For setup, examples, and controller-specific tips, see Remote control.

Command catalog

Twelve commands are supported by both protocols:

CommandEffect
nextAdvance the verse queue by one
prevMove back one in the queue
bible_nextAdvance the Bible Text panel by one verse (does not move the queue)
bible_prevMove the Bible Text panel back one verse
send_to_livePresent the previewed verse on the live output
add_to_queueAdd the previewed verse to the queue
showBring the overlay on-air
hideTake the overlay off-air
theme <value>Switch the active theme by name
opacity <value>Set overlay opacity (placeholder — wired but not yet effective)
confidence <value>Set the detector's confidence threshold
on_air <value>Force the on-air toggle to the given value

Commands are case-insensitive on input. value argument types vary: theme is a string, opacity and confidence are floats in [0.0, 1.0], and on_air is a boolean (true / false).

next, prev, bible_next, bible_prev, send_to_live, add_to_queue, show and hide take no argument.

Over OSC an extra argument is ignored, so a controller that sends a value with every button press works unchanged. Over HTTP the body must match exactly — adding a value key to an argument-free command returns 422.

bible_next and bible_prev step within the current chapter and continue into the adjacent chapter of the same book at its edges. They stop at a book boundary rather than rolling into the next book, and do nothing at all when there is no chapter on the other side.

send_to_live and add_to_queue act on the verse in Program preview. With nothing previewed, both are no-ops — send_to_live will never blank the live output.

OSC

OSC runs over UDP on port 8000 (configurable). Address pattern is /rhema/<command>. Type coercion is permissive — int, float, and bool argument types are coerced to the target command variant without error, so any controller's idea of how to send a number works.

AddressArgumentExample
/rhema/nextnoneoscsend localhost 8000 /rhema/next
/rhema/prevnoneoscsend localhost 8000 /rhema/prev
/rhema/bible_nextnoneoscsend localhost 8000 /rhema/bible_next
/rhema/bible_prevnoneoscsend localhost 8000 /rhema/bible_prev
/rhema/send_to_livenoneoscsend localhost 8000 /rhema/send_to_live
/rhema/add_to_queuenoneoscsend localhost 8000 /rhema/add_to_queue
/rhema/shownoneoscsend localhost 8000 /rhema/show
/rhema/hidenoneoscsend localhost 8000 /rhema/hide
/rhema/themestringoscsend localhost 8000 /rhema/theme s "Classic Dark"
/rhema/opacityfloatoscsend localhost 8000 /rhema/opacity f 0.75
/rhema/confidencefloatoscsend localhost 8000 /rhema/confidence f 0.8
/rhema/on_airbooloscsend localhost 8000 /rhema/on_air T

HTTP API

The HTTP server is built with Axum, listens on port 8080 (configurable), and exposes three endpoints under the /api/v1/ prefix.

GET /api/v1/health

A liveness check that returns the service identity:

curl http://localhost:8080/api/v1/health
{ "status": "ok", "service": "rhema", "version": "..." }

GET /api/v1/status

curl http://localhost:8080/api/v1/status

Returns a state snapshot from a Rust-side cache that the frontend refreshes once per second:

{
  "on_air": true,
  "active_theme": "Classic Dark",
  "live_verse": "John 3:16",
  "queue_length": 12,
  "confidence_threshold": 0.75
}

active_theme and live_verse are nullable — they're null until a theme is selected or a verse is sent live.

POST /api/v1/command

Dispatch any command from the catalog. The body is JSON with a command key and (for commands that take a parameter) a value key:

curl -X POST http://localhost:8080/api/v1/command \
  -H 'Content-Type: application/json' \
  -d '{"command":"theme","value":"Classic Dark"}'
{ "success": true }

error is present only on failure. Unknown commands and invalid JSON are rejected with success: false and a descriptive error string.

POST /api/v1/control is mounted as an alias for the same handler. Earlier builds mounted only that path while the docs advertised /api/v1/command, so the alias stays to keep existing integrations working. Write new ones against /api/v1/command.

CORS is permissive by default

The Axum router attaches CorsLayer::permissive(), so any origin on the network can call the API from a browser. That's convenient for local dashboards but unsafe to leave on once the listener is reachable beyond a trusted LAN — pair binding to 127.0.0.1 with a reverse proxy when you expose it.

Persistence and shutdown semantics

Both listeners can be started and stopped at runtime without restarting Rhema. Port-bind failures surface as visible errors in the Settings panel rather than silent listener crashes. Both listeners shut down cleanly on app exit — no zombie threads, no orphaned ports.

Authentication

By default both protocols are unauthenticated and bind to 0.0.0.0. For multi-machine deployments, route them through your network's existing auth (a reverse proxy with mTLS, a Tailscale ACL, etc.) — the API is intentionally simple and protocol-only so it composes with whatever you already trust. For local-only access, bind to 127.0.0.1 in Settings → Remote.

Status field reference

The keys returned by /api/v1/status:

FieldTypeMeaning
on_airbooleanWhether the broadcast overlay is currently on-air
active_themestringName of the currently selected theme
live_versestringReference of the verse currently displayed (e.g. "John 3:16")
queue_lengthintegerNumber of verses staged in the queue
confidence_thresholdnumberDetection confidence threshold in [0.0, 1.0]

On this page