Ghostty-powered terminal for jailbroken iOS — roothide and rootless bootstraps alike — built on libghostty-spm's terminal core with a host-managed I/O backend.
The app never spawns a process. ighostvtd, a root LaunchDaemon, owns every
terminal session and is the only component that can start one; the app reaches
it over a mach service and is entitled to nothing else. Sessions therefore
outlive the app — quitting detaches, and relaunching reattaches to the shells
still running.
| Path | Purpose |
|---|---|
iGhostVT/ |
The app: main.swift, Application/, Backend/, Interface/, Resources/ |
iGhostVTDaemon/ |
ighostvtd: the XPC proxy launchd starts |
iGhostVTIO/ |
ighostvtd-io: the child that owns every PTY, buffer, and shell |
iGhostVTDaemonShared/ |
Bootstrap paths, logging, and the proxy ⇄ io wire, in both |
iGhostVTCLI/ |
ighostvt-cli: the command-line client for those sessions |
iGhostVTWidgets/ |
WidgetKit appex: the Dynamic Island Live Activity |
Shared/Protocol/ |
XPC wire protocol, compiled into the app, the daemon, and the CLI |
Shared/Activity/ |
ActivityAttributes, compiled into the app and the appex |
Shared/Screen/ |
The replay-to-text renderer and key names, in the app and the CLI |
Configuration/ |
xcconfig files; Version.xcconfig holds the version number |
Packaging/ |
Debian control, maintainer scripts, ldid entitlements |
Packaging/macOS/ |
Bundled LaunchAgent and the (empty) entitlements for the Mac app |
Scripts/ |
xcodebuild wrapper, deb and macOS packagers, versioning |
Tests/ |
The PTY harness and the CLI screen-renderer tests |
Documents/ |
Architecture notes, research, and Site/ — the published page |
iGhostVT.xcodeproj is checked in (objectVersion 77, folder-synchronized
groups — adding a file to a synchronized folder adds it to the target).
libghostty-spm is consumed as a released package, not a sibling checkout.
See Documents/ARCHITECTURE.md for the ownership and data-flow design,
the jailbreak path rules, and per-window tabs.
make deb # unsigned iPhoneOS build, ldid ad-hoc sign, roothide .deb
make deb-rootless # the same binaries, packaged under /var/jb
make set-version VERSION=1.2.3Requires ldid and dpkg-deb (Homebrew). Both packages carry the same
arm64 binaries and differ only in layout: the roothide one declares
iphoneos-arm64e and installs /Applications/iGhostVT.app,
/usr/libexec/ighostvtd and the LaunchDaemon plist unprefixed, for the
bootstrap to relocate into the jbroot chosen at this boot; the rootless one
declares iphoneos-arm64 and installs the same three paths under /var/jb.
postinst bootstraps the daemon either way. ighostvt-cli ships inside the
app bundle, with /usr/bin/ighostvt-cli as a relative symlink to it.
ighostvt-cli drives the daemon's sessions from a shell — the same sessions
the app's tabs are showing. Every command is one shot; it never attaches, so
it neither takes a session from the app nor takes over your terminal.
ighostvt-cli list # id, foreground process, size, attached, directory
ighostvt-cli capture 1 # what that session's screen is showing, as text
ighostvt-cli capture 1 --full # with the scrollback the daemon still holds
ighostvt-cli send 1 text "ls -la" key Enter
ighostvt-cli send 1 key C-c # key names follow tmux's send-keys
ighostvt-cli new # open a session, print its id
ighostvt-cli new -- /bin/sh -l # or run something other than the shell
ighostvt-cli kill 1On the Mac it is /Applications/iGhostVT.app/Contents/MacOS/ighostvt-cli;
symlink it onto your PATH if you want it there. Exit codes: 0 success,
1 the daemon refused (no such session), 64 bad usage, 69 no daemon.
Every CLI verb is also a Shortcuts action (iOS 16 and later, and the Mac),
under the same rules: an action opens its own connection to the daemon,
never attaches, and closes it when done, so a session a tab is showing keeps
its tab. Run Terminal Command types a line, waits for the prompt to return,
and hands back what was printed; New Terminal Session, Send Text, Send Key,
Get Terminal Screen Text, Get Terminal Directory, Is Terminal Busy, Wait for
Terminal Prompt, and End Terminal Session are the pieces it is built from.
Show Terminal Session, Open New Terminal Tab, and Lock Terminal Session bring
the app forward and act on its tabs. Sessions are picked from a list that
names them by their foreground program and directory, and ighostvt://session/<id>
opens one from any URL.
make mac-zip # universal Release, ad-hoc signed → build/Packages/iGhostVT-<version>-macos.zipThe Mac build is the same two programs in one bundle. There is no package
manager to install a daemon, so ighostvtd ships inside the app at
Contents/MacOS/ighostvtd, launched by
Contents/Library/LaunchAgents/wiki.qaq.ighostvtd.plist, and the app registers
it with SMAppService the first time it runs. The helper then moves, updates,
and is deleted with the app instead of leaving an orphan in
~/Library/LaunchAgents.
To install: unzip → drag iGhostVT.app to /Applications → open it from
there. The order matters. Login Items binds to the path the app registered
from, and a first launch out of the Downloads folder — where Gatekeeper runs
it from a translocated, disappearing mount — would register a path that is gone
by the second launch. The app checks, and offers to move itself there (or
quit) rather than registering from the wrong place. macOS then asks once to
allow the background item; until that is granted the terminal says so instead
of spinning on "Connecting…". To remove the helper, turn iGhostVT off under
Login Items in System Settings — dragging the app to the Trash does not.
Neither the app nor the helper is sandboxed, and neither can be: macOS 14.2's
Background Task Management rejects a sandboxed app registering an unsandboxed
SMAppService job, and a sandboxed helper would hand its container to every
shell it spawns. Both carry Hardened Runtime instead.
Releases are ad-hoc signed — CI has no Developer ID — so Gatekeeper will refuse the download until the quarantine bit is cleared:
xattr -dr com.apple.quarantine /Applications/iGhostVT.appFor a build that needs none of that, sign locally with a Developer ID and notarize:
make mac-zip MAC_ZIP_IDENTITY="Developer ID Application: NAME (TEAMID)" \
MAC_NOTARY_PROFILE=ighostvt-notaryRequires macOS 13 or later.
make test # the PTY harness and the CLI's screen-renderer tests
make harness # just the daemon's spawn path, on macOS
make mac-run # the whole stack on a Mac: daemon as a LaunchAgent + Mac Catalyst appThe harness covers what is hardest to debug on a device: forkpty/execve,
the read loop, exit-status decoding, TIOCSWINSZ, descriptor hygiene, and the
path resolution of each jailbreak layout. It also drives the two requests the
CLI adds — reading a session's buffer and typing into it without attaching —
and Tests/CLIRenderer checks the screen model capture renders with. make mac-run builds ighostvtd for
macOS, loads it as a per-user LaunchAgent (make mac-daemon-uninstall removes
it), and opens the app built as Mac Catalyst against it — every part of the
product except what only the device has (GPU entitlement, bootstrap layouts,
Live Activities). Test those on device: run make deb, then install.