https://github.com/asmuelle/agent-ssh
Native macOS & iPadOS SSH workspace — terminal, SFTP, PostgreSQL, monitoring & network tools over a memory-safe Rust core bridged to Swift with UniFFI.
https://github.com/asmuelle/agent-ssh
developer-tools ffi ipados macos postgresql rust sftp ssh swift swiftui terminal uniffi
Last synced: about 18 hours ago
JSON representation
Native macOS & iPadOS SSH workspace — terminal, SFTP, PostgreSQL, monitoring & network tools over a memory-safe Rust core bridged to Swift with UniFFI.
- Host: GitHub
- URL: https://github.com/asmuelle/agent-ssh
- Owner: asmuelle
- License: agpl-3.0
- Created: 2026-06-05T15:53:14.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-07-01T07:54:44.000Z (6 days ago)
- Last Synced: 2026-07-01T09:25:07.660Z (6 days ago)
- Topics: developer-tools, ffi, ipados, macos, postgresql, rust, sftp, ssh, swift, swiftui, terminal, uniffi
- Language: Swift
- Homepage: https://asmuelle.github.io/agent-ssh/
- Size: 5.28 MB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: Security_Patch_Monitor.md
- Agents: AGENTS.md
- Cla: CLA.md
Awesome Lists containing this project
README
# agent-ssh
[](https://github.com/asmuelle/agent-ssh/actions/workflows/ci.yml)
Native macOS / iPadOS SSH workspace. AppKit + SwiftUI shell, SwiftTerm for the PTY, [`ssh-commander-core`](https://github.com/asmuelle/ssh-commander-core) for the protocol layer, [uniffi](https://mozilla.github.io/uniffi-rs/) for the FFI bridge.
## Stack
| Layer | Lives in | Notes |
|-------|----------|-------|
| App shell | `AgentSshApp/`, `AgentSshMobile/` | AppKit window + SwiftUI views, SwiftTerm |
| Swift framework | `Sources/AgentSshMacOS/` | Cross-target models, stores |
| FFI bridge | `src/` | Rust → Swift via uniffi proc-macros |
| Generated bindings | `bindings/` | `agent_ssh.swift`, `agent_sshFFI.h`, `module.modulemap` |
| Protocol layer | crates.io: `ssh-commander-core`, `ssh-commander-pg-parquet` | external |
| Xcode project | `Mc-Ssh.xcodeproj` | generated from `project.yml` by xcodegen |
## Prerequisites
- macOS 14+ with Xcode 15+ and command-line tools (`xcode-select --install`)
- Rust **1.95+** (edition 2024) — `rustup default stable`
- [`just`](https://github.com/casey/just) — `brew install just`
- [`xcodegen`](https://github.com/yonaskolb/XcodeGen) — `brew install xcodegen` (auto-installed by `just mac-bootstrap`)
```bash
just bootstrap # one-time: xcodegen + macOS + iOS Rust targets
```
## Build & run
```bash
# Native macOS app (ad-hoc signed, opens Finder window)
just mac-build
just mac-run
# iPad simulator (boots a sim, installs, launches)
just run-on-ipad "iPad Pro"
# Full local CI pass
just ci-local
```
Run `just` (no args) for the full recipe list.
## Common workflows
### Edit Rust FFI surface
```bash
# 1. Edit src/ffi/ (or src/lib.rs)
# 2. Regenerate Swift bindings
just mac-bindings
# 3. Rebuild
just mac-build
```
The bindings under `bindings/` are committed — regenerate and commit them whenever the FFI changes. Hand-patching `bindings/agent_ssh.swift` will appear to work but the per-function uniffi checksums will diverge from what the Rust lib reports at runtime, and `rshellInit()` will panic with `_assertionFailure`.
### Edit `project.yml`
```bash
just mac-gen # regenerate Mc-Ssh.xcodeproj
```
The xcodeproj is gitignored — `mac-gen` is automatic before any `mac-*` build via `_ensure-xcodeproj`.
### Sign + notarize a release DMG
```bash
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)"
export APPLE_ID="…"
export APPLE_TEAM_ID="…"
export APPLE_APP_SPECIFIC_PASSWORD="…"
just mac-release true # build → DMG → notarize → staple
```
For a build-only DMG without notarization, drop the env vars and run `just mac-release` (defaults to `false`).
### Sparkle auto-updates
```bash
just mac-sparkle-keygen # one-time, populate Info.plist with the public key
just mac-sparkle-appcast ./dist # generate appcast.xml from a folder of DMGs
```
## Repo layout
```
.
├── Cargo.toml # FFI crate manifest
├── build.rs # uniffi build hook
├── uniffi-bindgen.rs # bindgen entry point (cargo run --bin uniffi-bindgen)
├── src/ # Rust FFI bridge (bridge.rs, ffi.rs, monitor.rs)
├── bindings/ # generated Swift bindings (committed)
├── project.yml # XcodeGen manifest (single source of truth)
├── Mc-Ssh.xcodeproj/ # generated; gitignored
├── Package.swift # SPM wrapper around the static lib
├── Sources/AgentSshMacOS/ # cross-target Swift framework (models + stores)
├── AgentSshApp/ # macOS app target — SwiftUI views + managers
├── AgentSshMobile/ # iPadOS / iOS app target
├── Tests/ # XCTest harness (AgentSshMacOS + AgentSshApp + Beta smoke)
├── scripts/ # release, notarize, find-sparkle-tool helpers
├── justfile # command surface
├── README.md # you are here
├── AGENTS.md # AI-agent code tour
└── TOOLS.md # in-app feature catalog
```
## Troubleshooting
**`error: There is no XCFramework found at .../Sparkle.xcframework`** — stale Xcode SourcePackages cache pinned to the old absolute path. Wipe and regenerate:
```bash
rm -rf build .build Mc-Ssh.xcodeproj
just mac-gen
just mac-build
```
**`Thread 3 Crashed: rshellInit() → _assertionFailure`** — uniffi binding checksums don't match the rebuilt Rust lib. Always regenerate via `just mac-bindings` after touching the FFI; never hand-edit the generated Swift.
**Build is slow on first run** — `russh` and `parquet` (transitive via `ssh-commander-pg-parquet`) compile from source. Subsequent incremental builds are fast.
## Contributing
Contributions are welcome — see [CONTRIBUTING.md](CONTRIBUTING.md) for setup,
local checks, and the FFI workflow. All contributors agree to the
[CLA](CLA.md).
## License
[GNU AGPL-3.0](LICENSE) © 2026 Andreas Mueller. The project is also available
under separate commercial terms; reach out if AGPL doesn't fit your use.