Reverse-engineering the Roon desktop client's local protocol (TypeScript client + docs)

Hi all :waving_hand:

Like a lot of us, I’ve wished the API was more capable for years. The official node-roon-api is deliberately sandboxed — no metadata editing, no real library management, just a slice of what the desktop app can do. So I took the brute-force route as a tinkering project: watch what the Roon desktop client itself sends to the Core, work out the wire format, and see how much of it I could replay from my own code.

Write-up and docs: roon-internal-api | roon-internal-api

Code (MIT): https://github.com/arthursoares/roon-api-reverse-engineering

What it is. The desktop client talks to the Core over a private binary protocol that’s much richer than the extension API. I reverse-engineered it — helped enormously by the fact that the shipped Mono assemblies are unobfuscated, so it became more porting than guessing — and wrote a TypeScript client for it.

What actually worked (tried by hand, against one Core):

  • Reading the live object graph (zones, devices, now-playing, loaded library)

  • Favoriting an album (showed up in the UI)

  • Playing an album on a zone (audio came out)

  • Pause / standby / power

  • A reversible metadata edit (rating/title) — the original motivation, since the public API can’t do this

Big asterisk: the client also has generated typed wrappers for ~1550 methods, but only the handful above have been run live. The rest are generated from metadata and untested — correct-by-construction at best. And one subsystem is still fuzzy: streaming-catalog search (in-library search half-works).

This is a hobby experiment and help is very welcome — most of what’s left isn’t deep protocol work, it’s breadth and validation: confirming the generated methods against a real Core, and cracking that search subsystem. The contributing guide covers how to capture traffic and find your own Core’s details.

And to the Roon team if you’re reading: this is interoperability/learning only — it works solely against a Core you own, speaking a protocol your own client already uses on your own network. Happy to adjust or take it down if there’s any concern.

Curious what people think — and whether anyone else has gone down this road before.

2 Likes

@Arthur_Soares did a good job covering some of what I’m about to say in his documentation, but I want to restate it for the benefit of the community–

We don’t mind people tinkering with their own systems, and reverse engineering for the purpose of interoperability is generally not restricted legally unless encryption was broken, which is not the case here, so we are not going to do anything to interfere with this project.

That said, that this protocol is meant to be internal, and we can and do freely change it. We have a system of versioning and managing lockstep releases between clients/servers and we use it to make breaking changes fairly often. While this API exposes a lot of surface area, it does not document how to use the entry points properly, so there are some risks.

Misuse of this protocol can easily cause memory or compute leaks in the Roon Core. This protocol is engineered as an internal interface to be used by trusted code, and is not hardened for 3rd party use. There are requirements that must be followed carefully to ensure proper resource management across the protocol boundary. While Claude is at times a capable reverse engineer, I wouldn’t assume that it nailed the finer points of resource management across the protocol boundary.

The reason we support a more restricted official API is because that’s a surface area that we can commit to supporting indefinitely. This makes it a stable platform for building extensions that last years or decades without maintenance, but it also restricts it to the more core and immutable parts of the product.

In any case, I think that our support team delisted this thread because they weren’t sure about what to do with it. I’m going to make it visible again. Thanks for sharing, and hack on.

9 Likes

I think you are raising some very good points here @brian , but allow me to clarify why tinkerers are going this way: the native Roon API is missing a lot of functionalities that should be there out of the box. Just to name a few:

  • Playlist management (add / remove songs etc)
  • Full queue management (same)
  • Structure library search

Thank you, @brian. And a real thank you for putting the thread back up yourself. I didn’t expect that kind of openness, and it’s appreciated.

Your technical warning is the part I want to take seriously, because it’s also the part I’m least sure about. Talking to the Core correctly clearly involves obligations that aren’t visible from the outside — cleaning up what you open, ending subscriptions, closing the loop on callbacks — and those are easy to get quietly wrong when you’re working backwards from captured traffic. You’re right not to assume an AI pieced all of that together correctly. If there’s ever a hint you’d be willing to share about how a well-behaved client is supposed to clean up after itself, I’d document it carefully. Regardless, I’ll add a blunt warning to the repo: unsupported, liable to break on any update, and getting it wrong can burden the Core you’re running it against.

Related to that (since you’ve said you won’t stand in the way), I’m flipping the repository to public now so others can read it and check the work.

I do want to be straight about why any of this exists, though. The hours people sink into working around the edges of the official API aren’t really a gripe — they’re a sign of how much we like what you’ve built and how much we live inside it. Laurent’s list says it better than I can: playlists, queue control, real search. There’s clearly a group here that cares enough to take apart a binary protocol just to get a little closer to the product, and that seems like energy worth having on your side.

So I’m not asking you to throw the doors open; I get why the supported surface is small and deliberately so. I’m just wondering whether there’s room to grow it over time toward the things people keep bumping into. If there were ever an appetite for that, I’d bet a fair few folks on this forum would pitch in, carefully and however you’d want it done.

Either way — thanks for the generosity here. It genuinely made my week.

2 Likes