RheoclesREE-oh-kleez

Getting started

Install it, let macOS ask its questions, find the token, and record one take two ways — from the popover and from a shell.

Rheocles is a menu bar app that bundles a headless daemon, rheocles-core. The app is a thin client of the daemon over the same API you get. Launch the app and the daemon comes up on localhost:7447; quit the app and it stays until nothing is talking to it.

Install

Homebrew
brew install --cask artisan-build/tap/rheocles

Or download the disk image — every release is on the releases page with a zip and checksums beside it. The app, every nested binary and the disk image are Developer ID signed, notarized and stapled; Gatekeeper opens it without a right-click.

Requirements. Apple Silicon, macOS 15 Sequoia or later. The floor is Sequoia because that is where SCRecordingOutput and process-level Core Audio taps became dependable, and Rheocles uses both.

Permissions

macOS asks for each device class the first time it is needed, and Rheocles does not ask ahead of that — with one exception it cannot avoid.

macOS asks for when where it lives afterwards
a camera Camera the first arm System Settings → Privacy & Security → Camera
a microphone Microphone the first arm … → Microphone
system audio System Audio Recording the first arm … → Screen & System Audio Recording
displays and windows Screen Recording the first GET /streams — opening the popover … → Screen & System Audio Recording

The exception is Screen Recording: without it there are no displays to list, so nothing could ever be armed to trigger the prompt. The first stream list raises it once, and the grant takes effect on the daemon’s next launch — quit the app and open it again, and the displays are there.

The token

Every request needs a bearer token, on every transport. You do not have to go and get it. It is written to

~/Library/Application Support/Rheocles/token

with mode 0600, so any app running as you can read it and is paired with zero clicks. That is how Pteroprompter and the NativePHP app pair. Anything that cannot read a file gets the same token as a pairing code in the popover’s settings.

Rotating it — the button beside the code, or POST /token/rotate — writes a new token to the file and refuses the old one from the very next request, on both transports. Every client re-reads the file; a WebSocket that is already up must send { "auth" } again before its next command. Rotate it if the code was ever shown somewhere it should not have been.

Terminal window
TOKEN="$(cat ~/Library/Application\ Support/Rheocles/token)"
curl -s -H "Authorization: Bearer $TOKEN" localhost:7447/
GET / — discovery
{ "name": "Rheocles", "version": "0.1.0", "hostname": "studio.local",
"machineId": "…", "outputRoot": "/Users/len/Movies/Rheocles",
"freeBytes": 812345678912, "auth": "bearer",
"ports": { "http": 7447, "ws": 7448 } }

Everything Rheocles ever tells you about a file is a path relative to outputRoot — the take folder to the root, each file to the take folder. Store them as given; join them only if you need an absolute path. The root and the codec are the daemon’s two settings, GET and PATCH /settings, persisted beside the token in settings.json; the root cannot move while a take is active.

The first take, from the popover

  1. Click the mark in the menu bar. Every stream on the machine is listed — cameras, microphones, displays, system audio. Windows are hidden until Show windows is on in settings.
  2. Toggle arm on the ones you want. The row changes to ochre and the menu bar mark draws in outline: the device is live and costing CPU.
  3. Press Record. Every armed stream starts writing. The mark fills, the elapsed clock runs.
  4. Press Stop. The take folder under the output root has one file per stream and a manifest.json that describes them.

The first take, from a shell

The popover’s Record button is the one-click form, POST /record. The two-step form is what a client uses when it needs to know where the files will be before frame one:

create, cue, stop
H="Authorization: Bearer $(cat ~/Library/Application\ Support/Rheocles/token)"
B=localhost:7447
curl -s -H "$H" $B/streams # ids and armed state
curl -s -H "$H" -X POST $B/streams/camera:0x2300000fd9009c/arm -d '{"armed":true}'
curl -s -H "$H" -X POST $B/takes -d '{"name":"Episode 12"}' # → the manifest: id, paths; not recording
T=$B/takes/20260912T040433-fd9q
curl -s -H "$H" -X POST $T/start # the cue
curl -s -H "$H" -X POST $T/stop
curl -s -H "$H" $T # the manifest