libghostty-spm
August 2, 2026 · View on GitHub
Ghostty's terminal engine — renderer, input, parser, and the embeddable surface C API — packaged as a SwiftPM binary dependency for Apple-platform apps.
Consumers add this package and import GhosttyKit; SwiftPM fetches a
prebuilt, checksummed GhosttyKit.xcframework from a GitHub release.
libghostty's runtime resource tree (terminfo sentinel +
shell-integration + themes) ships in-repo via the companion
GhosttyKitResources product (Bundle.module) — no separate asset to
fetch. No zig, no Ghostty source, no local build on the consumer
side.
Why this exists
libghostty is only buildable from source with a very specific toolchain, and that build is fragile on current macOS:
- Ghostty pins an exact zig minor (0.15.x for the pinned commit). The
vanilla ziglang.org 0.15.2 tarball — and asdf/mise installs —
cannot link against the macOS 26 SDK: their bundled libSystem
stubs predate the SDK tbd change that dropped plain
arm64-macos. Homebrew'szig@0.15build patches this; Ghostty's own Nix devShell pins the brew variant for the same reason. - Xcode 26 split the Metal compiler into a separate component
(
xcodebuild -downloadComponent MetalToolchain). - The full libghostty static lib that exports the embedding C API is
only assembled when Ghostty's macOS app build runs; that app fails
to link on the macOS 26 SDK (
SwiftUICoreis now restricted), but the xcframework is produced before that failure. The build script tolerates the app-link failure and gates on a symbol check.
This package solves all of that once, here, instead of on every consumer's machine and CI.
Layout
Package.swift products: GhosttyKit (.binaryTarget url+checksum),
GhosttyKitResources (in-repo, Bundle.module)
Sources/GhosttyKitResources/ tracked resource tree, regenerated each build
vendor/ghostty/ git submodule @ pinned commit — source + pin
scripts/build-xcframework.sh brew zig@0.15, the load-bearing flags, symbol gate
scripts/release.sh build → ditto-zip → compute-checksum → gh release
The submodule gitlink is the Ghostty pin — git-native, no version
file. git submodule update --init vendor/ghostty checks out exactly
the built commit.
Building locally
brew install zig@0.15
xcodebuild -downloadComponent MetalToolchain # one-time, Xcode 26+
git submodule update --init vendor/ghostty
./scripts/build-xcframework.sh # → dist/GhosttyKit.xcframework
First cold build is several minutes (zig + Metal shaders). The script fails fast (one second) if the produced lib lacks the embedding C API.
Versioning
Package tags are independent SemVer (vX.Y.Z), not Ghostty's
version — Ghostty's pinned point is often an unreleased main commit
(no clean version to mirror) and the package has its own lifecycle
(build/script/resource fixes ship independently). Soft convention:
- MINOR bump when the Ghostty pin changes.
- PATCH bump for package-only fixes (build script, resources).
Traceability is exact and one lookup: every build writes a tracked
GHOSTTY_VERSION (git describe + full commit, e.g.
v1.3.1-927-ge90b7c9f), and each GitHub release's notes embed it. So
package tag → exact Ghostty is never ambiguous even though the numbers
don't match.
Cutting a release
.binaryTarget(url:checksum:) needs an asset that doesn't exist until
the release is published — resolved by pinning per tag:
git submodule update --init vendor/ghostty(verify the intended Ghostty commit)../scripts/release.sh vX.Y.Z— builds, zips, computes the checksum, and (withgh) creates thevX.Y.Zrelease withGhosttyKit.xcframework.zip(the only asset — resources ship in-repo, not as a release asset).- Commit the printed
repoSlug/releaseTag/checksumintoPackage.swifton thevX.Y.Ztag (move the tag to that commit). - Consumers pin
.package(url: ..., exact: "vX.Y.Z").
Bumping Ghostty
cd vendor/ghostty && git fetch origin && git checkout <new-commit>
cd ../.. && git add vendor/ghostty
Then cut a new release. Treat every bump as a ghostty.h API audit —
upstream is explicit that the libghostty C API is not yet versioned.
If a bump changes Ghostty's required zig minor, install the matching
keg-only Homebrew formula and update the version check in
scripts/build-xcframework.sh.
Consuming
.package(url: "https://github.com/sethdeckard/libghostty-spm", exact: "vX.Y.Z")
// target deps: "GhosttyKit", "GhosttyKitResources"
GhosttyKit is the upstream module (umbrella header ghostty.h).
GhosttyKitResources ships the runtime resource tree (terminfo
sentinel + shell-integration + themes) inside the package:
import GhosttyKitResources
// directory containing terminfo/ and ghostty/{shell-integration,themes}
let dir = GhosttyKitResources.directoryURL
Point libghostty at dir (via GHOSTTY_RESOURCES_DIR or the surface
config). No asset download, no extraction step.
License
MIT (LICENSE), matching Ghostty. That covers the packaging authored
here — the build and release scripts, Package.swift, the resources
target, and the docs.
THIRD-PARTY-NOTICES.md reproduces Ghostty's copyright notice and
lists what the xcframework and resource tree carry with them — read it
before shipping an app. Most components are MIT or BSD-style, though
several attach conditions of their own (Apache-2.0, MPL-2.0, OFL-1.1,
CC BY). Two merit special attention because they can affect what you
ship: a few Kitty-derived shell-integration scripts are GPL-3.0, and
libintl is statically linked under LGPL. Those apply whatever your
app is licensed as; a copyleft app just tends to satisfy them
incidentally. Neither changes this package's license.