swift-mosh
July 21, 2026 ยท View on GitHub
Pure Swift implementation of the Mosh protocol/client stack for Apple platforms.
This library focuses on protocol + transport + crypto + state processing only. Terminal UI/render adapters (Ghostty, SwiftUI terminal widgets, etc.) are intentionally out of scope so consumers can integrate with any renderer.
Features
- Pure Swift Mosh protocol/client implementation
- Split modules for protocol layers:
MoshCoreMoshTransportMoshWireMoshProtoLiteMoshCryptoOCBMoshCompressionMoshBootstrap(optional SSH handoff parser/helper)
- Real local
mosh-serverE2E test harness - 100% automated test coverage across
Sources/(via./scripts/coverage.sh)
Platforms
- iOS 16+
- macOS 13+
- Swift tools 6.0+
Installation
Add to Package.swift:
dependencies: [
.package(url: "https://github.com/wiedymi/swift-mosh", from: "0.1.0")
]
Then add products you need:
.target(
name: "YourApp",
dependencies: [
"MoshCore"
]
)
Quick start
import Foundation
import MoshCore
let endpoint = MoshEndpoint(
host: "127.0.0.1",
port: 60001,
keyBase64_22: "REPLACE_WITH_MOSH_CONNECT_KEY"
)
let session = MoshClientSession(endpoint: endpoint)
try await session.start()
try await session.enqueue(.keystrokes(Data("echo hello from swift-mosh\n".utf8)))
// Streaming host output for your renderer.
for await op in await session.hostOpStream() {
if case .hostBytes(let bytes) = op {
print(String(decoding: bytes, as: UTF8.self), terminator: "")
}
}
await session.stop()
App suspension and process relaunch
Freeze the live transport before the app is suspended, then persist the returned snapshot atomically:
let snapshot = try await session.prepareForApplicationBackground()
// If the process remains alive:
try await session.resumeFromApplicationBackground()
// After a process relaunch:
let restored = try await MoshClientSession.restore(from: snapshot)
try await restored.start()
MoshSnapshot contains the Mosh session key and buffered terminal protocol data.
Treat it as sensitive session material: keep the key in device-only secure storage,
protect the remaining checkpoint at rest, and delete both on explicit disconnect.
Reliability/lifecycle knobs are configurable in MoshClientConfig:
sendMinDelayMsackIntervalMsackDelayMsnetworkTimeoutMs(retained for snapshot/source compatibility; network silence does not end a Mosh session)heartbeatIntervalMsinitialRtoMsmaxRtoMsmaxRetransmitCount(caps retry bookkeeping; unacknowledged state remains pending)
Running tests
swift test
Coverage:
./scripts/coverage.sh
Run real local mosh-server E2E:
./scripts/run-real-e2e.sh
Or directly:
SWIFTMOSH_REAL_E2E=1 swift test --filter RealMoshServerE2ETests
Optional Bootstrap Helper
MoshBootstrap can parse noisy mosh-server output and launch a local server:
import MoshBootstrap
let connect = try MoshServerLauncher.launchLocalServer()
// connect.port, connect.key, connect.serverPID
Notes
- This package does not perform SSH negotiation itself.
- You are expected to handle auth/session bootstrap externally, then pass
host, UDPport, and 22-char key toMoshEndpoint.
- You are expected to handle auth/session bootstrap externally, then pass
- UI adapter is intentionally not included.
- Consumers should map
MoshHostOpto their own terminal backend/view layer.
- Consumers should map
License
MIT