Nearly all of my work happens through a connection manager. The one I had relied on for years, mRemoteNG, is Windows-only. So I wrote a native Mac app that reads exactly the same configuration file, and released it under the same licence as the original. This is the road from the need to the app, and why the giving-back part is not a formality.
The need
I work from a Mac. The connections live in confCons.xml — the file
mRemoteNG keeps its tree in: a large tree, with credentials inherited from folders
down to connections. Years of tidying and of my own conventions sit in that file.
There were two options, both bad. Either keep a Windows virtual machine running permanently just to open RDP and SSH sessions — a virtualisation layer between me and the keyboard, for something that should be instant. Or move to a Mac client and give up the tree: retype it by hand, connection by connection, or accept an approximate import that loses the structure and the inheritance. Neither is a solution; both are a tax paid daily.
The idea
Everything was decided by one simple question: what actually has to be preserved?
The answer was not the interface, it was the data. If the app reads
and writes exactly the same confCons.xml, byte-for-byte compatible, then
there is no migration at all. The same file lives on the Mac and on Windows, can be
synchronised between them, and if my app disappears tomorrow the file is still valid
for mRemoteNG. No lock-in, no conversion, no one-way door.
That meant reproducing the encryption too, not just the XML: the same key derivation and the same cipher mode, including the marker in the root that mRemoteNG uses to check the master password. A password set on the Mac has to open the file on Windows, and the other way round. That decision shaped everything after it — including things I would have liked to do differently and did not, because they would have broken compatibility.
I checked this directly in the mRemoteNG source: attributes it does not know are dropped on the first save. So private extensions to
confCons.xmlare simply not an option. Settings that belong only to the Mac app live in its own preferences, not in the shared file.
The work
The interface is SwiftUI. Underneath, each protocol has its own engine: RDP through FreeRDP 3 called from C, SSH/Telnet/SFTP through SwiftTerm, HTTP/HTTPS in a WKWebView — all of them in tabs, in one window.
The interesting part was not the writing, it was the diagnosing. A few concrete examples, because these are the kind of thing no tutorial covers:
- A crash when several RDP sessions opened at once. Not the codec, as I first assumed, but FreeRDP's tile-decoding thread pool, shared between sessions. Fixed by decoding on a single thread.
- RDP to Windows machines outside a domain failed to authenticate, reported misleadingly as a transport error. The real cause: NTLM computes its hash with MD4, which OpenSSL 3 moved into a separate "legacy" module that is not loaded by default.
- A Windows Server 2012 R2 cut the session a second after connecting. I was sending a scale factor of 200 in a field where the protocol allows only 100, 140 or 180 — because FreeRDP writes the two scale values crossed when it builds the single-monitor definition itself. Windows 10 and 11 ignore it; the older one does not.
- The seventh RDP connection would not open. macOS gives an app 256 open file descriptors, and an RDP session is expensive in them: every event and channel is a pipe. Six sessions plus a few terminals sit exactly on that ceiling, and the seventh dies deep inside the connect, with an error that looks like the server refusing it.
Every time the answer was not obvious, I looked for it in the mRemoteNG source instead of guessing. That is how I learned how it rotates its backups — copy before every save, then delete the oldest beyond a configurable count — and did the same, with the same default of 10. It is also how I learned why reordering tabs by dragging is a problem on Windows too: their docking library does not use the system API, it captures the mouse itself. After five failed attempts with the SwiftUI API, I ended up at the same solution.
The result
The app is called mRemoteNXT and it is in daily use, against the real tree my work depends on.
| Figure | Value |
|---|---|
| First public commit | 24 May 2026 |
| Releases since | 21 |
| Commits | 103 |
| Code (Swift, C, Objective-C) | ~7,400 lines across 25 files |
| Licence | GPL-2.0-or-later, same as mRemoteNG |
Protocols: SSH and SFTP, Telnet, RDP, HTTP/HTTPS, plus external tools run in a terminal tab. Multiple windows, each with its own configuration file, for anyone working across two monitors. Import from Royal TS. Signed, notarised automatic updates. VNC is next.
Back to the community
This app does not exist without mRemoteNG. I did not start from a blank page: I started from a file format someone else designed, an inheritance scheme that works, and a set of interface decisions refined over years of real use. When I did not know how something should be done, the answer was already written in their source.
That is why the licence is GPL-2.0-or-later, the same as theirs, rather than something permissive that would have left me more freedom. The code is public on GitHub: anyone who wants to use it, change it, or carry it further can. It seems the only honest answer when you build on someone else's work.
Thanks to the people who made mRemoteNG possible, as they appear in the project's own source: Felix Deimel (2007–2009, the original mRemote), Riley McArdle (2010–2013) and the mRemoteNG team since. An open-source connection manager relied on for two decades in that many server rooms is not a small thing.
What I took away
- The data format is the contract, not the interface. Choosing
to leave
confCons.xmluntouched removed migration, synchronisation and the fear of lock-in — out of a single decision made at the start. - Read the source of the project you want to be compatible with. Every important question was already answered there, and every time I assumed instead of reading, I lost time.
- A tight correlation is not a cause. I found a pattern that held three times out of three and called it the cause; it was coincidence, because both events were anchored to the same starting moment. The right test is not whether the symptom still happens, but whether the moment it happens changes.
- When the application log stops saying anything, reach for tcpdump. Two days of hypotheses were replaced by thirty seconds of capture, which showed the server sending an error packet and a reset two microseconds later.
- Run the official client alongside. Same machine, same path, same account, different client — the binary "works / does not work" moves an investigation from one place to another faster than any theory.
mRemoteNXT has its own page on cremenescu.ro, with screenshots and installation notes. It needs macOS 14 or newer, it is signed and notarised, and it sends nothing anywhere.