Skip to content

daiimus/geistty

Repository files navigation

DISCLAIMER: YOU HAVE ENTERED AI SLOP LUNCHLADY LAND.

This project was a proof of concept to determine that a LLM could not produce a shippable app. That objective failed, but this code is not good. I am currently in the process of redo'ing the Ghostty fork to, hopefully, better align with the projects architectural intentions and intend to finish this at some point. However, I would advise against citing this as anything more than functional slop.

Geistty

A native iOS/iPadOS SSH terminal powered by Ghostty's terminal engine

v0.1-stable iOS 17+ Swift 5.9+ Zig 0.14+

What is this?

iOS can't spawn local shells -- no fork, no exec, no PTY. Geistty works around this by using Ghostty's External termio backend, which accepts terminal data from an external source (SSH) instead of a local process. The result: real Ghostty terminal emulation -- Metal GPU rendering, full VT parsing, proper Unicode -- running on iPad and iPhone, connected over SSH.

tmux control mode (tmux -CC) is handled natively by Ghostty's Zig code. Multi-pane layouts, session persistence across app suspensions, and transparent reconnection all work.

See ARCHITECTURE.md for the deep dive on how everything fits together.

Current State

Tag: v0.1-stable -- deployed and confirmed working on iOS.

Everything below is implemented and functional:

Terminal

  • Full Ghostty terminal rendering (Metal GPU-accelerated, 120fps)
  • Complete xterm-256color/truecolor support
  • Text selection via long-press + drag
  • Copy/paste with system clipboard
  • Scrollback buffer with search (Cmd+F)
  • Mouse tracking for terminal apps (vim, htop, etc.)
  • Two-finger scroll and trackpad support

tmux Integration

  • Native tmux Control Mode (tmux -CC) via Ghostty's viewer.zig
  • Multi-pane layouts with per-pane output routing
  • Multi-window support with window tab bar
  • Per-window focused pane tracking via C API
  • Window tabs with switching (Cmd+1-9)
  • Ghostty-style keyboard shortcuts:
    • Split: Cmd+D (right), Cmd+Shift+D (down)
    • Navigate: Cmd+[ / ], Cmd+Option+Arrows
    • Zoom: Cmd+Shift+Enter
    • Equalize: Cmd+Ctrl+=
  • Window rename (Cmd+Shift+R, double-tap, context menu)
  • Session persistence across app suspensions
  • Touch-optimized divider dragging (30pt hit areas), double-tap zoom

Keyboard

  • Full hardware keyboard support (Magic Keyboard, external keyboards)
  • Arrow keys, function keys (F1-F12), Home/End/PageUp/PageDown
  • Modifier keys (Ctrl+C, Ctrl+D, Ctrl+Z, etc.)
  • On-screen keyboard with accessory bar (Esc, Ctrl toggle, arrows, Tab)
  • Keyboard resize with animated terminal reflow

Connections

  • SSH password and key authentication (Ed25519, RSA)
  • In-app SSH key generation
  • Saved connection profiles with favorites
  • Quick Connect
  • Secure credential storage (iOS Keychain)
  • Auto-reconnect on app resume (up to 3 retries, 2s delay)
  • Disconnect detection with reconnect overlay (Cmd+R)

Settings

  • Font size adjustment (Cmd+0/+/-)
  • Theme selection (18 bundled themes)
  • Font family (Departure Mono, JetBrains Mono, Fira Code, Hack, Source Code Pro, IBM Plex Mono, Inconsolata, Menlo, Courier New)
  • Config file (ghostty.conf) is source of truth
  • Auto-hiding chrome (header/toolbar)
  • Haptic feedback toggle

Architecture

SwiftUI (navigation, connection profiles, settings)
    |
UIKit Bridge (SurfaceView: Metal rendering + keyboard input)
    |
State & Transport (SSHSession, TerminalViewModel, TmuxSessionManager)
    |
GhosttyKit (Zig: VT parser, terminal grid, Metal renderer, External backend, tmux viewer)
    |
SwiftNIO-SSH (Network.framework transport)
    |
SSH Server (tmux -CC control mode)

Data flow is simple:

  1. Output: SSH bytes -> ghostty_surface_write_output() -> VT parse -> Metal render
  2. Input: Keyboard -> ghostty_surface_key() -> queueWrite() -> write callback -> SSH send
  3. tmux: Same paths, but Ghostty's viewer.zig intercepts -- wraps input in send-keys -H, parses %output for display

Full architecture documentation with Mermaid diagrams: ARCHITECTURE.md

Repositories

Repo Branch Purpose
daiimus/geistty main iOS app (Swift, SwiftUI, UIKit)
daiimus/ghostty ios-external-backend Ghostty fork (External backend, tmux viewer, iOS C API)
daiimus/swift-nio-ssh add-rsa-support SwiftNIO-SSH fork (RSA key support)

Building

Prerequisites

  • macOS with Xcode 15+
  • Zig 0.14+ (for building GhosttyKit)
  • iOS 17+ device or simulator
  • SSH key for git operations (via ssh-agent or similar)

Build GhosttyKit from Ghostty fork

The easiest way to rebuild GhosttyKit and validate everything locally:

cd path/to/geistty/Geistty
./ci.sh local-validate    # rebuild GhosttyKit + build Geistty + run tests

GhosttyKit is a generated artifact (gitignored) — it is built from the sibling ghostty repo and never committed. This command also auto-creates TestConfig.local.swift if missing, so fresh clones work without manual setup.

To rebuild GhosttyKit without running the full pipeline:

./ci.sh sync-ghostty      # rebuild and copy GhosttyKit

Or run the steps manually:

cd path/to/ghostty
zig build -Demit-xcframework=true -Dxcframework-target=universal

# Copy framework to Geistty
rm -rf path/to/geistty/Geistty/Frameworks/GhosttyKit.xcframework
cp -R macos/GhosttyKit.xcframework path/to/geistty/Geistty/Frameworks/

# Rename module maps to avoid conflicts with CSSH
for dir in path/to/geistty/Geistty/Frameworks/GhosttyKit.xcframework/*/Headers/; do
    [ -f "${dir}module.modulemap" ] && mv "${dir}module.modulemap" "${dir}GhosttyKit.modulemap"
done

Build and deploy to device

cd path/to/geistty/Geistty

# Full local pipeline (rebuild GhosttyKit + build + test)
./ci.sh local-validate

# Or just build + test without rebuilding GhosttyKit
./ci.sh all

# Build for device
xcodebuild -project Geistty.xcodeproj -scheme Geistty \
  -destination "platform=iOS,name=YourDevice" \
  -allowProvisioningUpdates build

# Install
xcrun devicectl device install app --device <device-uuid> path/to/Geistty.app

# Launch with console logging
xcrun devicectl device process launch --device <device-uuid> \
  --terminate-existing --console com.geistty.app

Run tests

# Swift tests (550 tests)
cd path/to/geistty/Geistty && ./ci.sh test

# Zig tests (External backend + tmux viewer)
cd path/to/ghostty && zig build test
cd path/to/ghostty && zig build test -Dtest-filter="tmux"

Project Structure

geistty/
├── ARCHITECTURE.md          # Deep architecture docs
├── AGENTS.md                # Agent development guide
├── Geistty/
│   ├── Geistty.xcodeproj
│   ├── Frameworks/
│   │   └── GhosttyKit.xcframework/
│   ├── Resources/
│   │   └── Fonts/           # 8 bundled font families
│   └── Sources/
│       ├── App/             # GeisttyApp, ContentView
│       ├── Auth/            # ConnectionProfile, Keychain, SSH keys
│       ├── Ghostty/         # C API bridge, SurfaceView, config, search, tmux protocol
│       ├── SSH/             # NIOSSHConnection, SSHSession, TmuxSessionManager
│       ├── Terminal/        # TerminalContainerView, VC extensions, multi-pane views, themes
│       └── UI/              # Connection list, editor, settings
├── GeisttyTests/            # 550 unit tests (20 files + 2 mocks)
└── GeisttyUITests/          # 4 UI test files + 2 config files

Dependencies

Dependency License Purpose
Ghostty MIT Terminal emulation engine (Zig, our fork)
SwiftNIO-SSH Apache 2.0 SSH transport (our fork with RSA support)
libxev MIT Event loop (used by Ghostty internally)
Departure Mono OFL Default terminal font

Acknowledgments

  • Ghostty by Mitchell Hashimoto -- the terminal engine that makes this possible
  • SwiftTerm by Miguel de Icaza -- referenced for iOS terminal patterns
  • SwiftNIO-SSH by Apple -- the SSH foundation we forked

License

MIT License -- see LICENSE.

Third-party dependencies are used under their respective licenses -- see LICENSES.md.

About

Native iOS/iPadOS SSH terminal powered by Ghostty's terminal engine

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors