Documentation

Everything Oreja does, and does not do.

Setup, the two permissions, what each panel state means, and the honest edges. Where a test was not run, this page says so.

Quick start

Oreja is a menu bar app for macOS with one job: coordinate Apple Music while the AI app you choose is speaking. You can pause and resume Music, or keep it playing at a lower volume and restore that volume when the reply ends.

What you need

  • macOS 14.2 or newer. The audio tap relies on Core Audio process taps.
  • ChatGPT desktop app (com.openai.chat, com.openai.chatgpt or com.openai.codex), or ChatGPT in Safari/Chrome as an experimental source.
  • The Music app, with a song loaded. An Apple Music subscription is not required by Oreja, but Music must be the app that is playing.
  • Command Line Tools if you build from source. There are no third-party dependencies.

Build and open

./scripts/build.sh
open build/Oreja.app

The first launch runs a six-step setup: welcome, AI source, music source, audio-capture permission, live test, finish. After that the app lives only in the menu bar. To see setup again, quit Oreja and run defaults delete app.oreja.van-gogh.

The build is ad-hoc signed and not notarized. On another Mac, macOS may block the first launch; allow it once in System Settings › Privacy & Security. Do not disable Gatekeeper for this.

Permissions

Oreja asks for two permissions, each with a specific purpose string.

PermissionWhyWhere
System audio recordingRead the selected app's output level through a Core Audio process tap.System Settings › Privacy & Security › Screen & System Audio Recording
Automation (Music)Pause, resume, or adjust the Music app's volume, depending on the selected mode.The macOS prompt on first control, then Privacy & Security › Automation

No microphone is needed by default, and Oreja never requests screen content, accessibility, full disk, contacts, location or network access. The optional “Pause while I speak” setting uses microphone level only. If you rebuild the app, macOS may treat it as a new app because its ad-hoc signature changed: grant audio capture again.

tccutil reset AudioCapture app.oreja.van-gogh
tccutil reset AppleEvents app.oreja.van-gogh

Using Oreja

  • The switch in the panel enables or disables coordination. Turning it off while Oreja owns a pause or volume change restores Music first.
  • Interruption mode lets you choose between pausing and resuming Music, or keeping it playing at a lower volume and restoring the original volume afterward.
  • Resume delay (0.3–3.0 s, default 0.8 s) is how long ChatGPT must stay quiet before music resumes. It is layered on top of the detector's 0.25 s silence window.
  • Pause while I speak optionally includes your microphone activity, so Music also responds while you answer.
  • Manual control always wins. If you pause Music yourself, Oreja will not resume it. If you resume Music while ChatGPT speaks, Oreja releases its claim.
  • Quitting while Oreja owns a pause restores playback before exit. The pause journal can restore it on the next launch, but only when the same track is still paused and the pause is recent.

Panel states

The panel shows one state at a time. This is the full list.

StateMeaningIf it stays there
Automation offThe switch is off; Oreja does nothing.Turn it on.
Looking for the AI sourceAutomation is on but no monitor is running.Open ChatGPT; check the selected source matches the app that is running.
Monitoring ChatGPTThe monitor is alive and the AI is quiet.Ask ChatGPT a question and watch for "ChatGPT is speaking".
Music playingThe AI is quiet and music is playing.Normal while ChatGPT is silent.
ChatGPT is speakingActivity detected; Music is paused or its volume is reduced, depending on the selected mode.If Music does not respond, check Automation permission for Music.
Music paused or volume reduced by OrejaOreja owns the current interruption.Music resumes or its original volume returns after the quiet interval.
Waiting for silenceThe AI stopped; the resume delay is counting.Adjust the delay in Settings.
Resuming musicA resume command is in flight.A failure is shown with a reason.
SuspendedThe Mac slept.Wake it; state reconciles automatically.
Permission requiredAudio capture or Automation is denied.See Troubleshooting below.
Needs attentionRepeated command failures or a monitor error.Open Diagnostics, fix the reason, then Retry.

Troubleshooting

Start with the panel: it shows one state and a reason. Diagnostics lists recent state transitions and a connection test with per-step results.

"Permission required" for audio

  1. Open System Settings › Privacy & Security › Screen & System Audio Recording.
  2. Enable Oreja, or remove it with "−" and relaunch Oreja to prompt again.
  3. Restart Oreja after changing the setting.
  4. If you rebuilt the app, its ad-hoc signature changed: grant again.

Do not grant Accessibility. Microphone access is optional and only needed for “Pause while I speak.”

Music never pauses

  • Check that the AI source selection matches the app that is talking. ChatGPT app versus browser is a common mismatch.
  • Check that Music is the app that is playing, not Spotify or a browser tab.
  • Watch Diagnostics for activity events. If none appear, the tap is not receiving audio: re-grant audio capture.
  • If activity appears but no pause is issued, Music was already paused, a command is pending, or a manual override was detected. Diagnostics says which.

Music keeps toggling

  • Increase the resume delay. Pauses shorter than the delay keep music paused.
  • Switch away from a browser source, or use a dedicated browser window: other tabs can look like AI audio.

Oreja left my music paused

  • If Oreja is running and still owns the pause, turn automation off or use "Resume music now".
  • If it crashed, relaunch it. The journal restores playback only when the same track is still paused and the pause is younger than 12 hours.
  • If Music quit or the track changed meanwhile, Oreja leaves playback alone. Press play in Music.

The app is not in the menu bar

  • It is a menu bar app with no Dock icon by design. Look for the listening glyph.
  • If the menu bar is full, quit another menu bar app or use a menu bar manager.
  • Launch it from Terminal to see errors: build/Oreja.app/Contents/MacOS/Oreja.

Privacy

Oreja is local-first and has no server. It does not record, transcribe, store or upload audio, prompts or replies.

DataCollectedStoredTransmitted
Conversation audioNoNoNo
Transcripts, prompts, repliesNoNoNo
Audio levels (dBFS)Computed in memoryDiscarded immediatelyNo
Track identity while pausedHeld in memoryUserDefaults, for crash recovery onlyNo
SettingsYesUserDefaults on this MacNo
Diagnostic eventsOreja's own state changesIn-memory buffer, up to 300 eventsNo
Account, analytics, crash reportingNoNoNo
Network requestsNone——

The app links no networking framework. It works offline, without an OpenAI API key and without an account. Remove all local state with defaults delete app.oreja.van-gogh.

Architecture

Oreja taps the output of the selected application's Core Audio processes, unmuted, so the app keeps playing normally while Oreja listens to a copy. The tap is private, targeted at that application's processes and helpers, and torn down when monitoring stops.

The decision to pause or resume lives in one deterministic state machine with no AppKit, no Core Audio and no AppleScript, so it is fully unit-tested. Platform code sits behind ports.

ChatGPT output audio → Core Audio process tap → level analysis → coordinator
                                                              │
Apple Music playback ← Apple Events ← pause / resume commands ┘
  • packages/OrejaCore — domain, ports, coordinator, settings, diagnostics. No platform imports.
  • packages/OrejaAudio — process discovery, taps, level sampling, activity detector.
  • packages/OrejaMedia — Apple Music control through Apple Events, plus a mock provider.
  • apps/macos — SwiftUI menu bar app, onboarding, settings, diagnostics.

Build and test

./scripts/build.sh            # build/Oreja.app, ad-hoc signed
./scripts/build.sh --probe    # plus the oreja-probe audio diagnostic
./scripts/build.sh --harness  # plus the real capture → Music harness
./scripts/build.sh --no-sign  # skip signing

./scripts/test.sh             # automated suite
./scripts/test.sh resume      # filter by test name

This machine has Command Line Tools only and its SwiftPM manifest library is broken, which is why the scripts invoke swiftc directly. Package.swift is kept for full Xcode installations.

Verify the audio path yourself

./build/bin/oreja-probe --list
./build/bin/oreja-probe --pid <pid> --seconds 6

The probe prints levels only; it never stores audio. Find a process with output=yes. If tap creation fails with OSStatus -50 or -4, audio-capture permission is missing for the app running the probe.

More documentation

This page covers what you need to run Oreja. The longer notes live in the repository, beside the app, as Markdown:

  • docs/architecture.mdThe full design and state machine.
  • docs/permissions.mdPurpose strings, reset commands, what not to grant.
  • docs/troubleshooting.mdThe longer troubleshooting guide.
  • docs/privacy.mdThe complete data statement.
  • docs/test-evidence.mdCommands, raw output, dates and limitations.
  • docs/compatibility.mdRelease, hardware and source matrix.
  • docs/distribution.mdSigning and notarization status.
  • docs/brand.mdPalette, type, voice and mark usage.