Rhema
Reference

Security

Content Security Policy, threat model, and how to report a vulnerability responsibly.

Rhema runs as a Tauri v2 desktop app, which means a Chromium-derived WebView with elevated privileges (filesystem, IPC, network). Compromising the WebView would compromise the host. We treat that seriously.

Content Security Policy

The Tauri WebView ships with a restrictive CSP defined verbatim in src-tauri/tauri.conf.json. The full directive list:

default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
font-src 'self' data:;
connect-src 'self';
media-src 'self' blob:;
worker-src 'self';
frame-src 'none';
frame-ancestors 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
manifest-src 'self'

A few things worth calling out:

  • script-src 'self' — no inline scripts, no eval, no third-party JS. The Tauri runtime injects its IPC bootstrap before the CSP applies.
  • style-src 'self' 'unsafe-inline' — required by Radix / shadcn for runtime style injection on portals and animations.
  • connect-src 'self' — the WebView itself only talks to the Tauri IPC channel. Outbound network traffic (Deepgram WebSocket, Bible data downloads) all happens from the Rust side, so the webview doesn't need to allow-list those endpoints.
  • frame-ancestors 'none' — the WebView can't be embedded anywhere.
  • object-src 'none' + base-uri 'self' — block plugin vectors and base-tag injection.

Reporting a vulnerability

Please report privately first

If you find a security issue, do not open a public GitHub issue. Send a private report by following the instructions in SECURITY.md in the repo.

Tauri capabilities

Rhema follows Tauri v2's principle-of-least-privilege capability system. Capability files live under src-tauri/capabilities/ — today there are two: default.json and desktop.json.

If you're reviewing the codebase for security:

  1. Start at src-tauri/capabilities/*.json to see which Tauri commands and plugins are reachable from each window.
  2. Trace each granted command to its handler in rhema-api.
  3. Audit the handler's input validation and side effects.

Remote control defaults — read carefully

Settings → Remote tab with bind-host fields highlighted, port-bind error states, and the live command log

Settings → Remote is where you'd swap the listener bind hosts from 0.0.0.0 to 127.0.0.1 when you don't want LAN-wide reachability. Port-bind failures show inline so you can spot a misconfigured port without leaving the dialog. Click to expand.

The OSC and HTTP listeners are not locked down by default. The shipped defaults are:

  • OSC: bind host 0.0.0.0, port 8000.
  • HTTP: bind host 0.0.0.0, port 8080, with CorsLayer::permissive() attached to the Axum router.

That means any device on the local network can drive the app and read its status without authentication. This is intentional for the LAN-only use case (Stream Deck on your operator's laptop), but it is unsafe to leave on once the listener is reachable from a broader network.

Before exposing Rhema beyond a trusted LAN:

  1. Bind both listeners to 127.0.0.1 from Settings → Remote.
  2. Or front them with a reverse proxy that adds authentication and TLS.
  3. Or restrict ingress at your firewall.

There is no built-in rate limit or auth on either protocol — the API is intentionally simple so it composes with whatever you already trust.

Defaults that protect the operator

  • No telemetry — Rhema does not phone home. There's no analytics, no crash reporter, no usage tracking.
  • Local-first by default — Whisper runs locally, the database lives on disk, and Deepgram is opt-in. No keys, no internet, no problem.
  • Bundled SQLiterusqlite with the bundled feature, so the binary doesn't link against a system SQLite that could be swapped out.

On this page