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.