RemminaMac is a powerful, native macOS remote connection manager inspired by the popular Linux tool Remmina. Built fully in SwiftUI and SwiftData, it provides a stunning, secure, and blazing-fast interface for managing remote connections, with opt-in credential storage rather than a hidden default backend.
Warning
This is a 0.9.1 beta and is not production ready. SSH has been used against real hosts by the maintainer; VNC and RDP have not yet been verified against live servers, so expect rough edges there. The automated test suite is deterministic, including the VNC suite running clean under Thread Sanitizer. The app bundle is ad-hoc signed, not notarized β see "Installing as a real macOS app" below.
Note: RemminaMac is an independent project and is not affiliated with, sponsored by, or endorsed by the Remmina project. The name reflects shared inspiration only.
First launch: no profiles yet. |
Sidebar and profile detail. |
A connected SSH session. |
Settings β Security: opt-in credential storage. |
- Credential Storage (opt-in, defaults to none):
SecuritySettings.credentialBackenddefaults to.noneβ aNullCredentialStorethat persists nothing. SSH keeps working via SSH keys /ssh-agentwith no backend at all. VNC and RDP need a saved password for their own protocol auth, so opt into one under Settings β Security β Credential Storage: the native macOS Keychain (kSecClassGenericPassword, iCloud Keychain sync explicitly opted out) or a portable AES-256-GCM-encrypted file (key derived with PBKDF2-HMAC-SHA256, 600,000 iterations) at~/Library/Application Support/RemminaMac/credentials.enc, unlocked with a passphrase you enter once per app run. No plain-text secrets either way. - Hostname Validation: Link-local addresses (169.254.0.0/16, fe80::/10 β including cloud metadata endpoints and IPv4-mapped IPv6), the zero address (0.0.0.0), encoded-IP notation (octal, hex, decimal, percent-encoded), shell metacharacters, control characters, and scheme-prefixed input (
http://,file://, etc.) are always rejected before any connection is attempted. Loopback and RFC 1918 private ranges are allowed by default β this is a client you point at hosts you choose, not a server guarding against SSRF β though the validator has opt-in flags for both; no setting in the app currently exposes them. - Strict Input Validation: Shell metacharacters in host fields and control characters in profile names are rejected. SSH key paths are validated with symlink-chain resolution and a dangerous-prefix allowlist.
- In-Memory Password Delivery: SSH and RDP credentials are written directly to the PTY in response to the server's
Password:prompt. There is no askpass helper script, no on-disk artifact, and nops auxleak. The previous askpass-pipe approach was retired because/usr/bin/sshcloses inherited file descriptors above 2 at startup before spawning the helper. - Safe-by-Default Remote Content: Remote β local clipboard writes (SwiftTerm OSC 52, VNC
ServerCutText) and remote β local URL opening are off by default and can be turned on per-user via Settings β Security.
- SSH Terminal: Tabbed PTY sessions with real-time PTY password delivery, no on-disk password artifacts, and bounded output buffers per session.
- VNC Support (Experimental): A native RFB 3.8 client implementation supporting Raw and CopyRect encodings, DES authentication, mouse/keyboard inputs, and server-bounded framebuffer parsing. VNC authentication is the classic DES challenge sent over a plain, unencrypted TCP socket β tunnel it over SSH on any network you don't fully trust. When idle, the client sends a periodic liveness probe instead of assuming the connection is dead. Tight/CoRRE/ZRRE encodings and reverse connections are not implemented yet.
- RDP Integration (Experimental): Prefers a locally installed native SDL FreeRDP client (
sdl-freerdp/sdl-freerdp3, frombrew install freerdp) over the X11xfreerdp, with PTY prompt-detection for password delivery; hands off to the Microsoft Remote Desktop URL scheme when no FreeRDP client is found. Not verified against live RDP servers. - SwiftData Profiles: Organize connections efficiently using tags, favorites, and searchable metadata.
- Visual Design: A beautifully crafted, responsive sidebar and detail view built entirely in modern SwiftUI.
- Extensive Logging: A fully isolated logging ring-buffer captures lifecycle events for easy debugging without leaking sensitive information.
- Enhanced VNC & RDP Capabilities (e.g., support for more encodings, embedded RDP engine, etc.)
- SFTP Drag-and-Drop File Browser
- Cloud Sync
- Idle VNC sessions no longer drop after 30 seconds (liveness probe + TCP keepalive).
- Deleting a profile closes its live sessions before removing stored credentials.
- Re-importing an export no longer duplicates profiles (exports carry ids).
- A passphrase-protected SSH key now gets an actionable
ssh-add --apple-use-keychaininstruction instead of a bare "Permission denied". - SSH failures are mapped to specific errors (host key changed, DNS, refused, timeout, unreachable) instead of one generic message.
- Selecting a profile no longer triggers a Keychain access-control prompt just to check whether a password exists.
- Unlocking the encrypted credential store no longer freezes the UI.
See CHANGELOG.md for the full list.
- macOS 14 Sonoma (or later)
- Xcode 16.3 (or later) β CI (
.github/workflows/build-test.yml) checks the installed toolchain and fails the build below Swift 6.1 - The test target uses Swift Testing, which needs Swift 6.0+; the actual
floor is 6.1, since SwiftTerm 1.10.1's own
Package.swiftuses a trailing-comma argument list (SE-0439) that only parses on Swift 6.1+
-
Clone the repository:
git clone https://github.com/Stormbringer-v1/remmina-mac.git cd remmina-mac -
Build the project: Using the Swift CLI:
swift build
Or simply open
Package.swiftin Xcode. -
Run the App:
swift run RemminaMac
Or select the
RemminaMacscheme in Xcode and press β + R.swift runis convenient for development, but it runs the binary directly rather than as a bundled macOS application β there is no icon, no Dock identity, and the app cannot be launched from Finder or Spotlight.
To build a proper .app bundle and install it:
./build_app.sh
cp -r dist/RemminaMac.app /Applications/build_app.sh compiles a release binary, assembles the bundle, generates the
icon set from Resources/AppIcon.png, applies the entitlements, stamps the
build number from the commit count, and ad-hoc code signs the result. It
prints the version it produced, for example Version: 0.9.1 (build 50).
Important
First launch will be blocked by Gatekeeper. The bundle is ad-hoc signed, not signed with an Apple Developer ID and not notarized, so macOS refuses to open it normally. To run it anyway, either right-click the app and choose Open (then confirm), or clear the quarantine flag:
xattr -dr com.apple.quarantine /Applications/RemminaMac.appA consequence of ad-hoc signing is that the code identity changes on every build, so macOS treats each build as a different application. If you've opted into the macOS Keychain as your credential backend (Settings β Security β Credential Storage β the default backend stores nothing at all), this means macOS will prompt for Keychain access again after an update.
- Unencrypted keys, and keys already loaded into
ssh-agent, work as-is β RemminaMac shells out to/usr/bin/sshwith-i <key>and leaves your agent's socket untouched. - Passphrase-protected keys need to be loaded first:
ssh-add --apple-use-keychain <key>. Ifsshstill prompts for a passphrase interactively, RemminaMac stops the connection attempt and shows that instruction instead of waiting on a prompt it can't answer. - Host keys are trusted on first use
(
StrictHostKeyChecking=accept-new). If a host key later changes, the connection is stopped with an error pointing at~/.ssh/known_hosts.
RemminaMac is backed by an extensive, robust test suite. Our test matrix covers hostname-validation bypass attempts (encoded IPs, link-local/metadata addresses), edge-case UI state roundtripping, credential-storage boundaries across the Keychain, encrypted-file, and null backends, VNC parser bounds, SSH PTY-write credential delivery, and stress-tested internal buffers.
Run the test suite via:
swift testRemminaMac employs a strict MVVM and Data-Store architecture to clearly separate side-effects from UI logic.
graph TD
UI[SwiftUI Views] --> ST[Stores @Observable]
ST -.-> PS[ProfileStore / SwiftData]
ST -.-> CS[CredentialStore: None / Keychain / EncryptedFile]
ST -.-> SS[SecuritySettings / UserDefaults]
ST -.-> CM[ConnectionManager]
CM --> SSH[SSHSession PTY]
CM --> VNC[VNCSession RFB 3.8]
CM --> RDP[RDPSession sdl-freerdp / xfreerdp / MSRD]
Power users love shortcuts. Navigate RemminaMac without touching your mouse:
| Shortcut | Action |
|---|---|
| β + N | New Profile |
| β + R | Reconnect Active Session |
| β + β§ + W | Disconnect Active Session |
β + W (Close Window) also works β it's the standard
macOS window shortcut, not something this app defines itself. The search
field's placeholder likewise references β + F, the
standard shortcut macOS gives a toolbar .searchable field; there's no
separate .keyboardShortcut binding for it in MainView.swift.
The macOS app sandbox is not enabled by default. Two architectural realities drive this:
/usr/bin/sshneeds to read and write the user's SSH config. A sandboxed app cannot reach~/.ssh/configor~/.ssh/known_hostswithout per-path entitlements.- RDP integrates with an externally installed FreeRDP client (
sdl-freerdppreferred,xfreerdpas a fallback). The sandbox blocks spawning of non-bundled, non-system executables on standard paths.
The Resources/RemminaMac.entitlements file is included as a forward-looking starting point for users who want to opt into sandbox with appropriate exceptions (for example, a bundled helper tool for the FreeRDP client, and explicit files.absolute-path.read-write exceptions for ~/.ssh). The build script applies this file via codesign --entitlements so what you ship matches the file on disk.
Keychain access is independent of the sandbox β the keychain-access-groups entry declares the credential namespace regardless β but it's only exercised at all when the macOS Keychain is the selected credential backend (Settings β Security β Credential Storage; the default backend stores nothing).
See CONTRIBUTING.md for build/test prerequisites, the branch and PR flow, and project conventions. If you're reporting a security issue rather than a bug, please read SECURITY.md instead of opening a public issue.
Distributed under the MIT License β see LICENSE for the full
text. Third-party framework and dependency licenses (Apple system
frameworks, SwiftTerm) are listed in LICENSES/README.md.




