Documentation

Help & FAQ

Baton is a free macOS music player for the library you already own: your self-hosted Navidrome or any Subsonic-compatible server. It plays with real depth (true gapless, crossfade, ReplayGain, a parametric EQ) and hosts a small control server so an AI agent can search, queue, and steer playback for you.

Getting connected

When you first open Baton, it asks you to connect to your music server.

  • Sign-in method: either a username & password (the classic Subsonic sign-in; Baton never sends your password in the clear, using the salted-token scheme Subsonic servers expect), or an API key if your server (e.g. a recent Navidrome) supports one.
  • Server URL: the full address of your server, e.g. https://music.example.com.
  • Username (password mode only) and your password or API key.
  • Click Connect. Baton verifies the connection before saving anything. If it works, your library loads in place; if not, it tells you why so you can fix the URL or credentials.

Your credentials are stored in the macOS Keychain, never in a plaintext file.

Multiple servers. Open Settings (⌘,) and choose the Servers pane to add more than one server and switch the active one. Baton keeps each server's credentials separately in the Keychain and reloads the library when you switch; your existing single connection is carried over automatically the first time.

Browsing your library

The left rail gives you the ways into your library:

  • Home (“For You”): a time-of-day greeting and tap-to-play shelves: Recently Played, “Because You Liked…” radio, Recently Added, Rediscover, and Your Mixes.
  • Search: songs, albums, and artists at once.
  • Mixes: auto-built mixes (Most Played, Fresh Additions, Top Rated, On Repeat, Forgotten Favorites, Discover) plus per-genre Daily Mix cards. Each has its own page to Play, Shuffle, Queue, or download the whole thing.
  • Albums: sortable by newest, recently added, most played, name, artist, track count, duration, rating, or at random; grid or list.
  • Artists: with bios and artist pages.
  • Playlists: your server-side playlists: create, rename, reorder, delete, and add or remove tracks (reordering saves back to the server).
  • Liked: everything you've hearted, split into Songs / Albums / Artists.
  • History: your local play log: Recent, Top Tracks, and Top Artists, over This Week / This Month / All Time.
  • Podcasts: the podcast channels your server hosts; episodes stream and queue just like tracks.
  • Radio: your server's internet-radio stations; add, edit, or remove a station (name + stream URL).
  • Downloads: everything saved for offline play, with total size on disk and an Offline-mode toggle.

Rating & liking. Tap the heart, or use the 5-star rating. Ratings are stored per-user on your server, so they follow you to any Subsonic client. Multi-select: shift-click a range, ⌘A to select all, then apply batch actions (like, add to queue, save as playlist, download, or mark for removal).

Mixes

Baton builds six standing mixes from how you listen: Most Played, Fresh Additions, Top Rated, On Repeat, Forgotten Favorites (liked, but not heard in the last month), and Discover, plus a per-genre Genres section. Each mix has its own page to Play, Shuffle, Queue, or download the whole thing. An agent can also build a mix to a length you ask for with music_build_mix.

History

The History tab is a local play log kept on your Mac: Recent, Tracks, Albums, and Artists, over This Week, This Month, or All Time. It's a free, local alternative to Last.fm or ListenBrainz, and you can export it as ListenBrainz JSON or CSV, import listens, or clear it.

Mark for Removal. Subsonic servers have no delete-file command, so Baton can't remove tracks from the server directly. "Mark for Removal" unlikes a track and rates it one star, a signal a separate server-side cleanup routine can read to prune it later.

Playing music

The Now-Playing bar

A persistent bar sits at the bottom of the window with the scrubber, transport controls, volume, the queue, a sleep timer, and the AirPlay picker. Collapse it to a slim strip to reclaim space (⌘⌃J) and expand it again the same way.

Full-screen Now Playing

Big artwork, an adaptive backdrop tinted from the album art, a waveform scrubber (for downloaded tracks), and side panels for Queue, Lyrics (synced karaoke-style when your server has them), and Related tracks. Space plays/pauses, Esc exits.

The floating mini-player

A borderless, always-on-top mini-player window (⌘⌥M): a compact card with the current track, artwork, scrubber, rating, and Up Next that you can keep in a corner while you work. On macOS 26+ it uses Liquid Glass.

The menu-bar controller

Baton also lives in the macOS menu bar, showing what's playing with Play/Pause, Next, and Previous, plus shortcuts to open the main window or the mini-player. It keeps Baton (and the control server) reachable in the background even when every window is closed.

Queue & modes

Drag to reorder, remove tracks, or clear the queue; Baton remembers it between launches and restores it paused. Shuffle keeps the current track playing and restores order when turned off. Repeat is Off / All / One. Continuous radio tops up the queue with similar tracks as you near the end.

Sleep timer

Set a timer from the Playback menu, or choose End of Track. Baton fades out gently (about 5 seconds) rather than cutting off.

Gapless, crossfade & sound quality

  • True gapless playback. Live albums and continuous mixes play with no silence between tracks. Baton pre-loads the next track (even prefetching network streams to disk); you can limit prefetch to Wi-Fi to avoid eating a metered connection.
  • Crossfade. Optionally overlap the end of one track into the next. (Crossfade and true-gapless are mutually exclusive: one overlaps, the other abuts.)
  • Loudness normalization (ReplayGain / R128). Even out volume across tracks or albums, using your server's ReplayGain data, with an adjustable preamp.
  • Parametric equalizer. Open the Equalizer from the Audio menu (⌥⌘E) or the Equalizer pane in Settings. Ten bands, each with adjustable frequency, Q, and gain, a live response curve, and presets (Flat, Bass Boost, Treble Boost, Vocal, Rock, Electronic, Loudness, Vocal Boost, Bass Reduce). Off by default, and bit-exact pass-through when off.

You don't have to set any of this up. After you've played about twenty tracks, Baton picks defaults from how you listen: gapless for album listeners, a gentle crossfade and autoplay for shuffle listeners, and it tells you what it did. Change any of it in Settings.

Downloads & offline

Baton can download tracks, albums, mixes, or playlists to a folder on your Mac and play them from disk, useful for gapless quality without re-streaming and for keeping favorites local. Downloaded tracks are preferred over streaming automatically.

The Downloads tab manages everything you've saved: total size on disk, play or delete any download, and a global Offline mode that keeps playback on local files.

Scrobbling

Baton can report your listens to your Navidrome/Subsonic server (updating its play counts) and to ListenBrainz and/or Last.fm if you add your account tokens. A track scrobbles once you've played about half of it (capped at a few minutes for long tracks), matching the usual convention.

Media keys & AirPlay

  • Media keys & Bluetooth remotes: the F7/F8/F9 keys and Bluetooth remote play/pause/next/previous/seek all control Baton, and the current track shows up in the macOS Now Playing widget.
  • AirPlay: use the AirPlay picker in the Now-Playing bar to send audio to an AirPlay device. (Chromecast / Sonos / UPnP casting is planned.)

Keyboard shortcuts

From the Playback menu, available anywhere in the app:

ActionShortcut
Play / Pause⌘⌃P
Next track⌘⌃ Right
Previous track⌘⌃ Left
Volume up / down⌘⌃ Up / ⌘⌃ Down
Mute / Unmute⌘⌃M
Minimize / expand player bar⌘⌃J
Open mini-player⌘⌥M
Equalizer⌥⌘E
Servers⌘,

The Playback menu also holds Shuffle, Repeat, and the Sleep Timer. In the full-screen player, Space toggles play/pause and Esc exits; in browse lists, ⌘A selects all.

Webhook actions

Webhook actions send a media item to an HTTP endpoint you choose. They're aimed at podcast episodes: for example, POST an episode's audio URL to a service that transcribes it. Set them up in Settings, in the Actions pane: give an action a name, pick a method (POST, GET, PUT, PATCH, DELETE) and a URL, add any headers, and write a body for methods that send one.

Anywhere you write a URL, header, or body you can drop in tokens that Baton fills from the episode: {title}, {channelTitle}, {enclosureUrl}, {feedUrl}, {guid}, {pubDate}, {durationSec}, {episodeImageUrl}, {channelImageUrl}, and {description}. Your actions appear in the ... / right-click menu on an episode, and in the multi-select bar. They only ever fire when you run them.

Spoken summaries: agents that talk

Baton's other agent-native feature is a voice. Through the speak_summary tool, an agent can have Baton say a short line out loud in a natural voice, so you hear “deploy finished, all green” instead of watching a screen for it. Baton can act on your music and it can talk back.

This really shines with more than one agent running at once. Give each agent, or each kind of task, its own voice, and you can tell them apart by ear without switching windows: the deploy agent in one voice, the research agent in another, an alert in a third. Practical for a busy multi-agent run, and genuinely fun. Configure it all in the Speech pane in Settings.

How an agent talks

speak_summary takes the line to say (text), a category that picks the voice from your map, an optional explicit voice or engine, and a mode: notify (a macOS notification with a Play button, the default), banner (an in-app banner), or auto (speak it right away).

It also takes a session: a short name for the agent itself, usually the repo it is working in. Baton shows it above the transcript in the summary window, so you can see who is talking without it being read to you, and remembers it for the rest of that agent's connection. The name also picks the voice: you keep a list of agent-and-voice pairs in the Speech pane, and anything not on it speaks in a voice from outside the list, so a project you named never sounds like one you did not.

On a Bluetooth speaker, Baton holds a moment of silence before it speaks. A Bluetooth speaker powers its radio down when idle and takes a moment to wake, which otherwise swallows the first word or two. The wait applies only over Bluetooth, so wired and built-in output are never delayed, and you can adjust it in the Speech pane if your speaker is slower than most.

One voice per agent

The category-to-voice map is where the multi-agent magic lives. Baton ships a starter map you can edit, extend, and preview:

CategoryVoiceGood for
defaultKokoro af_heartAnything with no category
researchKokoro af_bellaA research or analysis agent
deployKokoro am_michaelDeploys and builds
opsKokoro am_fenrirOps and infrastructure
alertKokoro af_novaAnything urgent
premiumChatterbox Emily.wavA cloned or premium voice
esKokoro ef_doraSpanish

Add a category per agent, pick the engine and voice, and preview it. A five-agent run becomes five distinct voices, and you always know who's talking.

The voices

Baton speaks through self-hosted, OpenAI-compatible text-to-speech servers on your own network, so the audio never leaves your machines: Kokoro for fast preset voices (the default) and Chatterbox for premium voices and voice cloning. Enter each server's address and use Test to check it.

You don't have to run any TTS servers. If none are set up, or one is unreachable, Baton falls back to the built-in macOS voice, so speak_summary still works and a line is never silently dropped (the fallback is on by default).

Reading what's on your screen

The same voice will read you a document. Select an article in Chrome or a wall of build output in a terminal, choose Services → Speak with Baton or press the shortcut you set, and Baton reads it while your music ducks underneath and comes back at the level it was. A window shows the text with the sentence being spoken highlighted.

Nothing watches your screen. Baton has no idea what is on it until you select something and ask, and a reading plays once and is then gone. Before it speaks a word, Baton removes anything shaped like a credential, strips terminal escape codes and shell prompts, and announces a block of code instead of pronouncing its punctuation. If you want to keep a reading, File → Save Reading as Audio… writes it to a file you choose, and only there.

An agent can hand over a document too. The read_aloud tool takes text an agent already has and reads it, which is the practical answer for a web page: pulling the article out of a page, rather than the navigation and the cookie banner, is real work, and an agent driving the browser has already done it. Pass text, optionally source (the site or app it came from, which labels the reading and can pick its voice), kind (browser, terminal or generic), and gist: true to have a long article summarized and the summary read instead.

Let an agent control your music

This is the feature Baton is built around: it can be driven by software, not just by you clicking buttons. Baton hosts a small control server on your Mac that speaks MCP (the Model Context Protocol), the same protocol Claude and other AI agents use to talk to tools. So you can say things like:

  • “Put on a 40-minute instrumental focus set.”
  • “What's playing? Like it.”
  • “Make a playlist of everything I liked this month.”
  • “Turn it down” / “skip this” / “play some jazz.”

…and the agent carries them out in Baton, searching your library, building a queue, starting playback, rating tracks, and creating playlists. The control surface exposes the same operations Baton's own UI uses, so anything the agent does is something you could have done by hand. You don't need an agent to use Baton; this is an extra surface on top of a complete player.

Four pieces, one pillar. Baton's agent side isn't a scattering of tricks — it's one connected capability: the MCP control server (the interface), audio focus (so another app can duck the music and hand it back politely), the menu-bar controller (which keeps Baton — and the server — reachable even with every window closed), and spoken summaries (so the agent can talk back). Together they mean an agent can connect, steer playback, duck around your dictation, and speak a result aloud — all with no window open.

Connecting a client

The server runs automatically while Baton is open, so there's nothing to switch on. When it starts, Baton writes a discovery file with everything a client needs at ~/Library/Application Support/Baton/mcp.json:

{
  "schemaVersion": 1,
  "name": "baton",
  "transport": "streamable-http",
  "url": "http://127.0.0.1:8787/mcp",
  "token": "b7f3c0…a1c9",
  "app": { "bundleId": "io.tonebox.baton", "version": "0.6.x" }
}

Point a client at the url over the Streamable HTTP transport and pass the token as a bearer token. In Claude Code that's one command:

claude mcp add --transport http baton \
  http://127.0.0.1:8787/mcp \
  --header "Authorization: Bearer <token-from-mcp.json>"

For Claude Desktop, or any client that uses an mcpServers config block:

{
  "mcpServers": {
    "baton": {
      "type": "http",
      "url": "http://127.0.0.1:8787/mcp",
      "headers": { "Authorization": "Bearer <token-from-mcp.json>" }
    }
  }
}
The default port is 8787, but if it's taken Baton uses the next free port and writes the real one into mcp.json. Always read the current url and token from the file. Baton has to be running for the server to be live, which is what the menu-bar item is for.

The tools

The server exposes 40 music operations, the same actions Baton's own UI uses.

Search & play

ToolWhat it does
music_searchSearch songs, albums, and artists
music_playPlay a search now, replacing the queue
music_play_nextInsert matches right after the current track
music_queue_addAdd matches to the end of the queue
music_play_playlistPlay one of your playlists
music_start_radioEndless “more like this” from a track or search
music_build_mixBuild a mix to a target length, then queue or save it

Explore the library

Searching answers “is this in here”. These answer “what is in here”, which is what a recommendation has to start from.

ToolWhat it does
music_list_genresThe genres in your library, with song counts
music_browse_albumsAlbums by kind: random, newest, most-played, liked, by genre or year
music_similar_songsReal neighbours of a track or artist, not a keyword match
music_discover_externalMusic you don’t have yet, from public catalogues. Off unless you turn it on
music_likedYour liked songs, albums, and artists
music_randomRandom songs, optionally within a genre or year range
music_artist_infoAn artist’s biography and every album of theirs you have
music_transcriptWhat was said in a spoken track, timed. Returns a window, not the whole hour
music_summarize_trackAn episode’s summary and its timestamped chapter marks

Control playback

ToolWhat it does
music_pause / music_resume / music_stopPause, resume, or stop
music_next / music_previousSkip forward or back
music_seekJump to a position in the current track
music_set_volumeSet Baton's volume, 0 to 100
music_set_repeat / music_set_shuffleRepeat (off/all/one) and shuffle
music_sleep_timerPause after some minutes
music_set_crossfadeCrossfade length between tracks in seconds (0 is off)

Queue, rating & playlists

ToolWhat it does
music_get_queueReturn the queue with positions
music_reorder_queue / music_remove_from_queueMove or remove a queued track
music_likeLike or unlike a track
music_rateSet a 1 to 5 star rating
music_create_playlist / music_add_to_playlistCreate a playlist or add to one
music_delete_playlist / music_list_playlistsDelete or list playlists
music_get_playlistRead one playlist’s tracks

Report & sound

ToolWhat it does
music_now_playingReport the current track, state, and queue position
music_recent_eventsWhat was played recently and how long each was heard before moving on, including skips
music_set_eqTurn the equalizer on/off and apply a preset

Live resources

Baton also publishes five read-only views a client reads once and is then notified about when they change, so an agent can follow along without polling:

ResourceWhat it holds
baton://now-playingCurrent track, state, position, volume
baton://queueThe full queue and current index
baton://library/playlistsYour playlists
baton://library/likedYour liked songs, albums, and artists
baton://history/recentRecently played, plus top tracks and artists

Audio focus & speaking aloud

Two more operations, audio_suspend and audio_resume, let another app duck or pause Baton politely and bring it back. This is how Tonebox lowers the music while you dictate, then restores it, but only if you didn't change playback yourself in the meantime. And speak_summary lets an agent have Baton read a short result aloud in a natural voice (see Spoken summaries).

How it's secured: the control server listens only on your own machine (loopback, 127.0.0.1), so it isn't reachable from your network, and every request must present the secret token Baton generates. The token is compared in constant time, and the mcp.json that carries it is readable only by you. Both are required together.

Status: the control server is live. It speaks MCP Streamable HTTP on loopback (default port 8787), exposes 28 music_* operations (including music_build_mix), the audio_suspend / audio_resume focus hand-off, and speak_summary; publishes five live resources; and writes the mcp.json discovery file with the endpoint URL and token. A menu-bar controller ships too.

Control Baton from Telegram or Discord

Baton can take commands from a chat app, so the stereo answers to your phone from the couch, the kitchen, or the other end of a train line. Set it up in Settings → Remote.

This doesn't open your Mac to the internet. Baton dials out to Telegram and Discord and waits for them to hand it messages, exactly as a chat app on your Mac does. No port is opened, nothing needs forwarding on your router, and the loopback-only control server above is untouched.

Telegram, step by step

  1. In Telegram, message @BotFather (Telegram's official bot-maker — look for the verified checkmark) and send /newbot.
  2. Give it a display name, then a username ending in bot.
  3. BotFather replies with a token like 8123456789:AAF3k9…. Treat it like a password.
  4. In Baton: Settings → Remote, turn on Enable remote control and Enable Telegram, paste the token, click Save. Status turns green with your bot's @name.
  5. Message your bot /link 123456 using the six-digit code from the pane's Linking section. It replies “Linked.”

Group chats: Telegram bots default to privacy mode in groups — np goes unseen while /np gets through. Use /-commands in groups, or turn privacy off via BotFather's /setprivacy. /pause@your_bot disambiguates when several bots share a group.

Discord, step by step

The order matters: the Message Content intent comes before the token, because without it Discord strips the text out of your messages — the bot connects fine and appears to ignore everything, with no error anywhere.

  1. At discord.com/developers/applications, click New Application and name it.
  2. Open Bot in the sidebar and set three things: Public Bot off; Message Content Intent on (under Privileged Gateway Intents — then Save Changes); Reset Token and copy it (shown once).
  3. In Baton: Settings → Remote, enable remote control and Discord, paste the token, Save. Status turns green when the connection is up.
  4. Invite the bot to a server: copy the Application ID from General Information, then open
    https://discord.com/oauth2/authorize?client_id=YOUR_APPLICATION_ID&permissions=68608&scope=bot
    and pick your server. 68608 = View Channels + Send Messages + Read Message History, and nothing more — no Administrator, no Manage-anything. (No server? Create a private one with Discord's + button.)
  5. Pick or create a channel. Baton ignores messages written by bots, so it can share a channel with webhook feeds.
  6. Send /link 123456 there, with the code from Baton's Linking section.

To confine the bot to particular channels, put channel ids into Limit to channels (get an id via Discord Settings → Advanced → Developer Mode, then right-click a channel → Copy Channel ID). Optional — the link-code allowlist is what actually guards access.

Why the link code

A bot token identifies the bot, not you — so Baton doesn't treat it as permission. Anyone who found your bot could otherwise message it and take over your speakers. Until a chat is linked Baton ignores everything it's sent, and each code works only once, because you're typing it into a chat history. Linked chats are listed in the Remote pane with a Revoke button.

What you can say

Ordinary messages; the leading slash is optional.

MessageWhat it does
play kind of blueSearch the library and play the best match
pause · resume · stopThe obvious ones
next · prevSkip forward or back
queue radioheadAdd matches to the end of the queue
vol 40Volume 0–100 (Baton's own, not the Mac's)
seek 1:30Jump to a position
npWhat's playing right now
like · rate 5Star or rate the current track
playlists · playlist EveningList playlists, or play one (part of the name is enough)
mix upbeat focusBuild a mix from a description
radioEndless “more like this”
sleep 30Pause in 30 minutes (sleep off cancels)
forgetClear what this chat remembers
helpThe full list, in the chat

Replies carry buttons for the common actions, so skipping a track is a tap rather than a typed word.

Saying it in your own words

Turn on Understand plain English and anything Baton doesn't recognize as a command is read as intent instead — “put on something mellow”, “make me a 40-minute driving mix”. This needs a model to talk to, which you provide; Baton ships no key and the feature stays off until you set it up. A Test button sends one real request and tells you whether it worked.

Baton speaks two API dialects, which between them cover nearly everything:

  • Anthropic — Claude's Messages API.
  • OpenAI-compatible — the chat/completions shape served by OpenAI, Groq, Together, Mistral, DeepSeek and OpenRouter, and by self-hosted vLLM, Ollama, LM Studio, llama.cpp and LiteLLM. Pick this one for a model running on your own hardware.
macOS gates local-network access separately. If Baton has not been allowed, requests to a model on your own network fail with “The Internet connection appears to be offline” even while Baton is plainly online. Switch Baton on under System Settings → Privacy & Security → Local Network.

Your own model. Set the provider to OpenAI-compatible, put your server's address in API base URL — the root that /chat/completions hangs off, e.g. http://your-server:8000/v1, or http://localhost:11434/v1 for Ollama — and set Model to whatever name it serves. Nothing leaves your network and there's no per-message cost. The one requirement is that the model supports tool calling: Baton asks it to pick from a list of commands, and a model without that support fails the Test with an error saying so.

Commands are tried first, but they don’t trap you. A message starting with a command word is handled instantly and locally — play kind of blue is a search, not a question for the model. If that literal reading finds nothing and plain English is on, Baton asks the model what you meant instead of leaving you with “no songs matched”, so play the second one works even though it starts with play.

Follow-ups work. Each chat remembers its last few exchanges, so “show me tracks for Dido” followed by “play the second one” does what you’d expect. That memory lives only while Baton runs, covers a few recent exchanges, expires after about half an hour of quiet, and is separate per chat. Send forget to clear it.

It's the only part of Baton that talks to a third party. What leaves your Mac is the sentence you typed and the list of Baton's own commands — never your server credentials, and never your listening history. Whether anything from your library goes with it is decided by one further setting, below. Under the hood the model picks from the same control surface an agent gets, so a sentence can't ask Baton to do anything the buttons can't; deleting a playlist is deliberately out of reach for it.

Letting it look around first

Underneath is a second switch, Let it look around first, and it is the difference between a translator and something you can have a conversation with.

Off, your sentence becomes one command chosen blind. The model has never seen your library, so it has to be right first time about music it knows nothing about. Ask for “lazy music” and you get “nothing matched” — even though the chillout is right there tagged chill, lounge and ambient, because a mood is rarely a song title.

On, Baton lets it look before it answers. It can read which genres your library actually uses, what you have liked and played most, what is similar to a track, and which albums are newest or most-played — then search again with words it has seen work, and play the result. The reply says what it did and why it is not literally what you asked: “nothing called ‘lazy’ — your chillout is tagged chill, playing that”.

It asks when asking is the honest thing. If what it found splits two ways and the answer changes what you would hear — two artists sharing a name, a six-hour mix beside a forty-minute set — it offers them as buttons, each with the fact that decides it. Tap one, or type 2, or say “the ambient one”. Ignore it and after a minute or so it starts the one it recommended and tells you so, rather than leaving you in silence. Send anything else and the question is dropped, so nothing starts playing under you after you have moved on.

This is the setting that changes what leaves your Mac, which is why it is separate and off by default. Looking around means what it finds — song titles, artists, genres — travels to the model with your question, because that is the only way it can answer. With it off, none of your library ever does. Point the base URL at a model on your own machine and the distinction stops mattering: nothing leaves your network either way.

It costs a little more than one request per message, since looking takes turns. Against a local model that is typically a second or two.

It knows what you listen to. Before it answers, Baton hands it a dozen lines read from your own server — biggest genres with counts, what you have played most and how often, how many songs you have liked, what you added recently. Nothing stored, nothing guessed; the server’s own numbers, read fresh each day. That is what makes “what kind of music do I listen to?” answerable and “surprise me” grounded in your collection.

It occasionally has a view — when a number is worth mentioning it says so in passing, then does what you asked anyway. It never refuses, and mentions any one thing at most once a day.

What it remembers

Some things your server cannot know: that you do not want vocals while you work, that the gothic playlists are your partner’s, that “my trance” means the Classic Trance ones. Tell Baton and it keeps them, in a plain file you can open.

  • It stores your words, not its impressions. Every memory carries the sentence you actually said. There is nowhere to put “seems to like sad music on Sundays” — the file has no field for a guess about you.
  • It tells you every time it writes one, in the same window a second later. One message fixes a misunderstanding.
  • One command away. memories lists everything with its quote, forget 2 removes one, forget everything clears the lot. Settings has a switch and a delete-all button.

Updates

Baton updates with Sparkle (the standard macOS updater): there's a Check for Updates item in the app menu, and Baton checks its own feed and installs signed, notarized builds. You can download the current release from baton.tonebox.io; it updates itself from there on.

Roadmap

Now in Baton (recently landed): Podcasts and Internet radio tabs, a Downloads / offline manager, a parametric Equalizer, multiple servers / account switching, and the agent-native music_build_mix.

Still ahead (called out so the docs stay honest):

  • iOS / iPadOS companion: listen away from the desk.
  • Casting beyond AirPlay: Chromecast, Sonos, and UPnP/DLNA. (AirPlay works today.)
  • Sonic-analysis mixes: built from the sound of your music (tempo/energy/key), not just play history.
  • Crossfeed / additional DSP and a lyrics fallback when your server has none.

FAQ

Is Baton free?

Yes, a give-away from Tonebox. No subscription, no catalog rent; you're playing music you already own on a server you already run.

How is Baton different from Tonebox?

Two products from the same maker. Tonebox is a paid, local-first notes app that records and transcribes meetings and voice notes. Baton is the music player, pulled out into its own free app. Tonebox can control Baton (for example, ducking the music while you dictate), but you don't need Tonebox to use Baton.

What servers work with Baton?

Navidrome and any Subsonic-compatible server. Baton speaks the Subsonic API, so most self-hosted music servers in that family work. It has no catalog of its own; there's nothing to browse until you connect a server.

Is my password safe?

Yes. Credentials live in the macOS Keychain, never in a plaintext file. With username & password sign-in, Baton uses the salted-token scheme Subsonic expects, so your password isn't sent in the clear on each request.

Does Baton phone home?

Not by default. Baton talks only to the music server you point it at, plus the scrobbling services (ListenBrainz / Last.fm) and its own update feed if you turn them on. It has no catalog server of its own to call. The one opt-in exception is crash reporting: if you turn on Send crash & error reports (Settings, About, Diagnostics, off by default), Baton sends crash and error data to its developer via Sentry to help fix bugs, never your music, library, server address, or account, and no IP or identifiers.

Is the control server a security risk?

No, by design. It listens only on your own Mac (loopback), so it isn't reachable from your network, and every request must carry a secret token Baton generates. Both are required.

Do I need an agent to use Baton?

No. Baton is a complete, click-to-play music player on its own. Agent control is an extra surface, not a requirement.

Does Baton work offline?

Yes, for downloaded music. Download tracks, albums, mixes, and playlists and play them from disk without re-streaming; the Downloads tab manages them, with a global Offline mode. Browsing new content and streaming still need your server to be reachable.

Can I cast to speakers?

AirPlay works today, from the AirPlay picker in the Now-Playing bar. Chromecast, Sonos, and UPnP/DLNA are planned.

What platforms does Baton run on?

macOS (15 or later). An iOS / iPadOS companion is on the roadmap; Windows and Linux clients are not planned.

Back to baton.tonebox.io