Skip to content

About

Native macOS, iPadOS and iOS client for the ghost remote desktop (GDP over QUIC). Swift 6, SwiftUI, Metal and VideoToolbox; Apple silicon only.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

The Spectre icon

spectre-apple

Spectre, a native client for the ghost remote desktop on Mac, iPad and iPhone

Platforms Swift 6 License: MIT

English | 简体中文 | 繁體中文 | 日本語 | Español

Spectre on the Mac, connected to a GNOME desktop on an Ubuntu host

ghost is an open-source remote desktop system: a host (wraith) streams a Linux desktop over the GDP protocol, and a client shows it and sends input back. spectre-apple brings that client to Apple devices as a native app, written in Swift with SwiftUI, Metal and VideoToolbox, on top of ghost's own protocol library, libgdp.

spectre-apple is an independent, third-party client inspired by ghost; it is not part of the ghost project. See Acknowledgements.

Features

Signing in

  • Sign in to a ghost server with your user name and password; further prompts, such as a one-time code, are asked in turn. Then choose a device and a desktop session, or take over one that is already running.
  • Trust on first use: you confirm the server's certificate fingerprint on the first connection, and it is remembered. Trusted Servers… on the login screen lists the remembered fingerprints and lets you forget them.
  • Tick Remember to keep a login in the Keychain, on this device only; a recent login then connects with one tap.
  • A lost connection (Wi-Fi changing, the Mac going to sleep, iOS suspending the app) reconnects by itself.

Picture and sound

  • H.264 and HEVC, decoded in hardware by VideoToolbox and shown with Metal without copying frames; frame loss and latency are reported back to the host.
  • The remote desktop is resized to fit the window, or the screen on iPad and iPhone. Resolution in the toolbar turns this off, or asks for the full Retina resolution.
  • The remote desktop's sound, with a jitter buffer that conceals lost packets, and a mute button.
  • Your microphone, sent to the remote desktop while you turn it on.

Input

  • Keys are sent by physical position, so the remote keyboard layout decides what they type.
  • Mouse with every button and smooth scrolling, and a captured mode with relative motion for games and 3D views. The remote cursor is drawn locally.
  • On iPad: hardware keyboards, trackpads and mice, and fingers in either a direct-touch or a trackpad mode.
  • On iPhone and iPad: the on-screen keyboard types on the remote desktop, with a key bar for Esc, Tab, modifiers, arrows and function keys.

Sharing

  • Text clipboard in both directions, off until you switch it on.
  • Up to four game controllers, forwarded as standard gamepads.

And also

  • Disconnect (the remote session keeps running) or log out (it ends).
  • The interface is in English, Simplified Chinese, Traditional Chinese, Japanese and Spanish, following the system's language.

Roadmap

Not done yet:

  • The lossless refinement layer (refine), and the AV1 and PyroWave codecs
  • Echo cancellation for the microphone
  • Checking certificates issued by a certificate authority on iOS (for now they are trusted on first use, like any other)

Screenshots

Signing in on the Mac iPad in trackpad mode
The login screen on the Mac A session on iPad in trackpad mode, with the remote cursor drawn by Metal

Requirements

  • A Mac with Apple silicon running macOS 27, or an iPhone or iPad running iOS or iPadOS 27 (arm64 only)
  • A ghost host to connect to (ghostd and wraith); see the ghost project for setting one up

Getting started

The Mac app is on GitHub Releases: download the .dmg, open it and drag Spectre to Applications. It is signed with a Developer ID and notarized by Apple.

For iPhone and iPad, or to work on Spectre, build it from source as below.

Tools

  • Xcode 27, with the iOS 27 simulator runtime
  • XcodeGen
  • cmake (3.25 or later) and ninja: brew install cmake ninja
  • vcpkg, ideally at the commit upstream's CI uses:
git clone https://github.com/microsoft/vcpkg ~/src/vcpkg
git -C ~/src/vcpkg checkout 07f4812200df3d3c931c0c8a6081d3b21fe2bf9f
~/src/vcpkg/bootstrap-vcpkg.sh -disableMetrics

If you keep vcpkg somewhere other than ~/src/vcpkg, set VCPKG_ROOT.

Build

  1. Clone the repository with the upstream ghost submodule (in an existing clone, run git submodule update --init):

    git clone --recurse-submodules https://github.com/pokoblin/spectre-apple.git
  2. Build GDPCore.xcframework, which holds libgdp, its dependencies and a small C bridge. It is written to Packages/GDPKit/Artifacts/ and is not committed. The first build takes a few minutes; later ones reuse vcpkg's binary cache. Logs go to build/logs/.

    scripts/build-gdpcore.sh

    It builds the macos, ios and ios-simulator slices; name some of them to build only those, for example scripts/build-gdpcore.sh macos.

  3. Generate the Xcode project, then open Spectre.xcodeproj and run the Spectre scheme:

    xcodegen generate

    Or build the Mac app from the command line:

    xcodebuild -project Spectre.xcodeproj -scheme Spectre -destination 'platform=macOS,arch=arm64' -configuration Release -derivedDataPath build/DerivedData build
  4. To keep the Mac app, copy it to Applications:

    ditto build/DerivedData/Build/Products/Release/Spectre.app /Applications/Spectre.app

To run Spectre on an iPhone or iPad, choose your development team for the target in Xcode.

Usage

Spectre opens on the login screen. Enter the server (host or host:port; the port defaults to 4442), your user name and password, and choose a desktop. On the first connection you are asked to confirm the server's certificate fingerprint. If the server asks for anything else, such as a one-time code, it is asked on its own; leave the password empty to answer every prompt that way.

Keyboard and mouse

Cmd is sent as Super (the Windows key), so copy and paste on the remote desktop are still Ctrl+C and Ctrl+V. Only these shortcuts stay on your Mac:

Shortcut Action
Ctrl+Cmd+G Capture or release the mouse (also the Capture Mouse button in the toolbar)
Ctrl+Cmd+F Full screen
Cmd+Q Quit

Touch on iPad and iPhone

The toolbar switches between two modes:

  • Touch: the pointer goes where you touch. Tap to click, long-press to right-click, drag with one finger to drag, drag with two fingers to scroll, and tap with two or three fingers for a right or middle click.
  • Trackpad: the screen works like a trackpad. Move the cursor with one finger and tap to click.

Sound and microphone

The remote desktop's sound plays whenever the host sends it (wraith does when it can capture the session's audio). Mute in the toolbar silences it on this device only, and the setting is remembered.

Microphone in the toolbar sends your microphone to the remote desktop, where it becomes the session's default recording device. It starts off in every session, and the system asks for permission the first time. There is no echo cancellation yet, so use headphones, or the remote desktop will hear its own sound.

Development

Tests

swift test --package-path Packages/GDPKit
swift test --package-path Packages/SpectreKit

Both packages include an in-process test host and test lobby, so the end-to-end tests run over a real QUIC connection on the loopback interface. SpectreKit's tests also decode H.264 and HEVC colour bars and render them with Metal. To run them in the iPad simulator:

(cd Packages/GDPKit && xcodebuild test -scheme GDPKit -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M5),OS=27.0' -derivedDataPath ../../build/DerivedData-GDPKit)
(cd Packages/SpectreKit && xcodebuild test -scheme SpectreKit -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M5),OS=27.0' -derivedDataPath ../../build/DerivedData-SpectreKit)

Running without a server

With any of the options below, Spectre skips the login and starts playing. It first encodes a 10-second 1080p60 test stream with VideoToolbox (about 3 seconds), then loops it. Decode, presentation and session statistics appear at the top left and are printed to stdout every second; the toolbar switches the source, the codec and the presentation pacing. There are three sources:

  • Loopback (the default): a test host inside the app sends the stream over a real GDP session (QUIC on 127.0.0.1). It also beeps once a second, to test sound.
  • Direct: the stream goes straight to the decoder, with no network.
  • Remote: a wraith started with a static token (§6.11 of the specification):
build/DerivedData/Build/Products/Release/Spectre.app/Contents/MacOS/Spectre --connect HOST:PORT --token TOKEN --fingerprint SHA256

Benchmark mode measures for N seconds after a 2-second warm-up, prints the statistics and quits:

build/DerivedData/Build/Products/Release/Spectre.app/Contents/MacOS/Spectre --bench 60 --codec h265

Other options: --source loopback|direct, --codec h264|h265, --mode onArrival|displayLink, --in-flight <N, 0 for no limit>, --screen <screen index>, --rate <display link Hz>, --min-present-ms <ms>, and --host-clipboard <text> (the loopback host's initial clipboard).

iOS and iPadOS simulator

xcodebuild -project Spectre.xcodeproj -scheme Spectre -destination 'platform=iOS Simulator,name=iPad Pro 13-inch (M5),OS=27.0' -configuration Release -derivedDataPath build/DerivedData build
xcrun simctl install booted build/DerivedData/Build/Products/Release-iphonesimulator/Spectre.app
xcrun simctl launch --console-pty booted org.ghost.spectre.apple --source loopback

On iOS the app starts from a UIKit app delegate, so that its root view controller can lock the pointer (see docs/input.md). If a build from before that change is installed, uninstall it once with xcrun simctl uninstall booted org.ghost.spectre.apple; otherwise the system restores the old window and it stays black.

The simulator decodes video in software and reports no presentedTime, so its timings can't be compared with a device or a Mac.

Project layout

Path Contents
project.yml The XcodeGen project: one multiplatform app target for macOS and iOS
App/ The app: per-platform entry points, the login and session UI, the icon
Packages/GDPKit/ The C bridge to libgdp (Native/) and its Swift wrapper: sessions, lobby, test host
Packages/SpectreKit/ Shared code: the session, VideoToolbox decoding, Metal presentation, input, the login flow
scripts/ Builds the dependencies and the xcframework
docs/ Design notes and measurements, in Simplified Chinese
experiments/ One-off experiments, such as a Swift/C++ interop probe; not part of the app
third_party/ghost/ Upstream ghost, as a read-only git submodule

Design notes

The notes under docs/ record how each part works, what was measured and what went wrong along the way. They are written in Simplified Chinese.

  • prototype-findings.md: the prototype's three technical risks and how each was settled (calling libgdp from Swift, zero-copy presentation, cross-compiling for iOS)
  • session-client.md: the session layer, checked against §6–§11 of the specification
  • login.md: lobby login and certificate trust
  • saved-logins.md: saved passwords in the Keychain, one-tap sign-in
  • reconnect.md: reconnecting a lost session, replaying the login, iOS in the background
  • keep-alive.md: QUIC keep-alive, and how long a lost connection takes to notice
  • resolution.md: resizing the remote desktop to fit the window
  • input.md: keyboard, mouse, cursor and touch
  • keyboard.md: the on-screen keyboard on iOS, characters to keys, the key bar
  • audio.md: sound, the jitter buffer, Opus decoding, the microphone
  • clipboard.md: the clipboard on the Mac and on iPad, echo suppression, the paste prompt
  • gamepad.md: game controllers, the button mapping, the Home button
  • localization.md: the interface languages, the glossary, adding a string
  • app-icon.md: the app icon and how to change it

Contributing

Issues and pull requests are welcome; for larger changes, please open an issue first. A few things to know:

  • The client is written from the GDP specification (third_party/ghost/docs/spec/gdp-spec.md) and public standards. Most of upstream ghost is GPL-3.0, so please don't copy or port code from it, including upstream's own spectre client; only libgdp/ and the specification are MIT.
  • Run both packages' tests before sending a change, and update the matching note under docs/ when behaviour changes.
  • New interface text needs translations for every language in the String Catalogs; see docs/localization.md.

Acknowledgements

spectre-apple is inspired by ghost by Joseph Saiko, a complete remote desktop system made up of the GDP protocol, the wraith host and the spectre client for Linux. This project uses ghost's protocol library, libgdp, is written from ghost's published protocol specification and design documents, and borrows the name Spectre from the upstream client. Many thanks to the author for open-sourcing ghost and for releasing libgdp and the protocol specification under the MIT license; this project would not exist without them.

spectre-apple is an independent, third-party client and is not part of the ghost project.

License

MIT.

  • In upstream ghost, libgdp/ and the protocol specification are MIT, and everything else (including upstream's own spectre client) is GPL-3.0-only. This project builds only libgdp/; its client code is written from the specification and public standards, and no code from the GPL parts is copied or ported.
  • The app links ngtcp2, OpenSSL, Protocol Buffers, Abseil and zstd statically, all under permissive licenses (MIT, Apache-2.0, BSD-3-Clause). A binary distribution must include their notices: see THIRD_PARTY_NOTICES.md.

About

Native macOS, iPadOS and iOS client for the ghost remote desktop (GDP over QUIC). Swift 6, SwiftUI, Metal and VideoToolbox; Apple silicon only.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages