⚠️ WORK IN PROGRESS — NOT FULLY FUNCTIONALThis project is under active development and is not ready for use. It was built using AI-assisted "vibe coding" — code was generated by AI agents working from a detailed plan, not hand-crafted by a human who understood every line. Treat it accordingly: there are bugs, untested edge cases, and rough edges. Review everything before relying on any of it.
An iOS 18+ SSH terminal client for iPhone and iPad, built on SwiftNIO SSH and SwiftTerm.
- SSH connections with password auth (RFC 4252
password, secrets in Keychain) and public-key auth (ed25519 from Keychain, P-256 from Secure Enclave) - ProxyJump / jump chains up to 5 hops with per-hop host-key verification
- TOFU host-key trust — fingerprint prompt on first connect, hard reject on changed keys
- In-app SSH agent with per-request authorization, session cache, auto-deny when backgrounded
- Terminal UI — SwiftTerm-based, hardware keyboard, IME/CJK composition, multi-window on iPad
- Multiple concurrent sessions — session switcher with detach/reattach that preserves terminal state, on iPhone and iPad
- Graceful reconnect — background suspends, foreground re-handshakes; no auto-reconnect after kill
- Coder REST integration — multi-server config, workspace discovery, agent picker, start policy, connect to running workspaces
- Coder tailnet tunnel (default flavor) — native
coder ssh-style connectivity over the userspace tailnet core (CoderNet.xcframework, AGPL), compiled in behind theCODER_TUNNELflag. The AppStore flavor excludes it entirely and uses direct SSH instead. - Herdr client — workspace handshake, native surface rendering, semantic keyboard/focus/resize input, clipboard text and bounded image paste, probe diagnostics, detach/reconnect
- Transport abstraction — SSH is one conformer; ET/mosh can be added later without touching session layers
- Live herdr-server validation on this dev host — building the herdr v0.9.0 server fixture needs zig 0.15.x, which fails to link libSystem on macOS 26 (toolchain-vs-host issue, not an app defect). Persistence and lifecycle tests currently run against committed-frame replay. See
.omo/evidence/phase2-h16-server-fixture.md. - No mosh or Eternal Terminal — architecture supports adding them, but they are not implemented in v1.
- Pointer/touch routing for herdr panes, graphics scenes, OSC 8 safe-open — triaged as future-phase work in
Docs/HERDR-RELEASE-TRACEABILITY.md.
- Keyboard-interactive auth (NIOSSH has no keyboard-interactive client; password and public-key only)
- RSA keys / key export
- SFTP/SCP or port forwarding
- Terminal transcript/scrollback persistence
- Analytics
- ssh_config/known_hosts import
- Custom CA/self-signed Coder TLS
BicTermCore (Swift package — no SwiftUI/UIKit)
├── SSH/ SSH transport, agent codec, ProxyJump
├── Transport/ TerminalTransport protocol + capability descriptors
├── Sessions/ Session registry, reconnect engine
├── Keys/ Keychain repo, OpenSSH parser, Secure Enclave
├── Trust/ Host-key TOFU verifier
├── Coder/ REST client, token store, workspace model
└── Models/ Connection, Hop, SessionSnapshot, etc.
BicTerm (iOS app)
├── App/ SwiftUI shell, scene manifest
├── Terminal/ SwiftTerm integration
├── Sessions/ Session UI, trust prompts
├── Connections/ Connection list + editor
├── Keys/ Key management UI
├── Coder/ Coder server config pane
├── Agent/ Agent authorization UI
└── Design/ Tokens, dark-first appearance
External tools the build, test, and fixture chain needs. SwiftTerm 1.20.0 and
swift-nio-ssh 0.15.0 are vendored under Vendor/ and need no separate install;
do not brew anything for them.
| Tool | Minimum version | Why it's needed | Install (macOS) |
|---|---|---|---|
| Xcode | 26, plus the iOS 26.3 simulator runtime | Builds the app; provides xcodebuild, xcrun, clang, dsymutil, and the simulators every script targets |
Mac App Store or developer.apple.com, then xcode-select --install |
| XcodeGen | 2.x | Generates BicTerm.xcodeproj from project.yml; required before any Xcode or xcodebuild run |
brew install xcodegen |
| Go | any recent release (the CoderNet/go.mod toolchain directive pins go1.26.5 and Go auto-downloads it) |
Builds CoderNet.xcframework via scripts/build-coder-net.sh (go build -buildmode=c-archive); also builds the derp-proxy in scripts/test-coder-derp.sh |
brew install go |
| Python 3 | 3.9+ (stdlib only) | Runs the Coder API stub and UDS forwarder (Fixtures/coder/stub.py, Fixtures/bin/uds-forward.py) and the JSON/token helpers inside fixtures-up.sh and coder-dev-up.sh |
Ships with the Xcode Command Line Tools; Homebrew alternative: brew install python |
| Rust (rustup + cargo) | stable, with targets aarch64-apple-ios and aarch64-apple-ios-sim |
Builds the herdr FFI core in scripts/build-herdr-core.sh and Vendor/herdr/check.sh. Both scripts override RUSTUP_HOME to the repo-local .build-artifacts/rustup, so add the targets with that env set: RUSTUP_HOME=.build-artifacts/rustup rustup target add aarch64-apple-ios aarch64-apple-ios-sim |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh (or brew install rustup-init + rustup-init) |
| cbindgen | pinned by cargo install --locked |
Generates HerdrCore.h; build-herdr-core.sh installs it repo-locally into .build-artifacts/tools/ on first run |
No action needed |
| cargo-deny | latest | License and advisory policy checks in Vendor/herdr/check.sh |
cargo install cargo-deny |
| jq | 1.6+ | License inventory assembly in Vendor/herdr/check.sh |
brew install jq |
OpenSSH (/usr/sbin/sshd, ssh, ssh-keygen), nc, curl, openssl, unzip |
system versions | SSH fixtures on ports 12222/12223, Coder binary download and password generation | Preinstalled on macOS; nothing to install |
| Ruby | system Ruby (stdlib only) | Coder acceptance helpers (scripts/*.rb) |
Preinstalled on macOS; nothing to install |
| xcbeautify | any (optional) | Prettier xcodebuild output in build-appstore.sh, test-core.sh, test-ui.sh; all three fall back to raw logs when absent |
brew install xcbeautify |
| cargo-audit, cargo-fuzz | latest (optional, hardening only) | Advisory audits and the herdr fuzz targets under Vendor/herdr/herdr-ios-ffi/fuzz/ (fuzz needs a nightly toolchain). Neither is installed on the current dev machine, and nothing in the normal build/test chain requires them |
cargo install cargo-audit cargo-fuzz |
| Feature | Required tools |
|---|---|
Basic app build (xcodegen generate, open Xcode, build) |
Xcode, XcodeGen |
Default flavor, scheme BicTerm (CoderNet tailnet tunnel) |
Above, plus Go, then scripts/build-coder-net.sh |
AppStore flavor (scripts/build-appstore.sh) |
Xcode, XcodeGen (xcbeautify optional). No Go, no tunnel core |
Test fixtures (scripts/fixtures-up.sh) |
Python 3 and the preinstalled OpenSSH/curl tools |
Coder native dev fixture (scripts/coder-dev-up.sh) |
Python 3, curl, openssl, unzip, system Ruby. The script downloads and sha256-verifies the pinned coder v2.36.4 binary itself; PostgreSQL is embedded in coder server, so there is no database to install |
herdr FFI build (scripts/build-herdr-core.sh) |
Rust with both iOS targets (cbindgen self-installs) |
Hardening / SBOM (Vendor/herdr/check.sh, fuzz targets) |
cargo-deny, jq; cargo-audit and cargo-fuzz for the optional audit/fuzz passes |
Not required: Docker (no container is used anywhere in the fixture flow), and zig. The live herdr server fixture would need zig 0.15.x, which fails to link on macOS 26 (see "What Doesn't Work Yet"), but no build or test script invokes zig.
brew install xcodegen go python jq
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
RUSTUP_HOME=.build-artifacts/rustup rustup target add aarch64-apple-ios aarch64-apple-ios-sim
cargo install cargo-deny # only needed for Vendor/herdr/check.shxcodebuild -version # Xcode 26+
xcodegen --version # 2.x
go version # any recent Go; go.mod pins the exact toolchain
python3 --version # 3.9+
cargo --version # rustup-managed stable
RUSTUP_HOME=.build-artifacts/rustup rustup target list --installed | grep apple-ios # both iOS targets
jq --version
/usr/sbin/sshd -? 2>&1 | head -1 # system sshd presentInstall the tools above first: see Dependencies.
# Prerequisites: Xcode 26+, iOS 26.3 simulator runtime
xcodegen generate
open BicTerm.xcodeproj
# Default (open-source) flavor: scheme BicTerm, destination iPhone 17 Pro
# or iPad Pro 13-inch (M5). Requires the tunnel core:
scripts/build-coder-net.sh # builds .build-artifacts/coder-net/CoderNet.xcframework (AGPL)
# AppStore flavor (no AGPL code, no tunnel): script wrapper, scheme BicTerm-AppStore
scripts/build-appstore.sh # AppStore-Release by default; pass AppStore-Debug to debugThe fixtures and suites below need the tools listed under Dependencies.
# Start local fixtures (sshd on 12222/12223, Coder stub on 18080)
scripts/fixtures-up.sh
# Core unit/integration tests
scripts/test-core.sh
# UI tests
scripts/test-ui.sh
# Tear down fixtures
scripts/fixtures-down.sh- swift-nio-ssh 0.15.0 (Apache 2.0) — vendored fork with agent-forwarding patches
- SwiftTerm 1.20.0 (MIT) — vendored fork
See DEPENDENCIES.md for the full license inventory.
TBD — not yet licensed. All rights reserved until decided.
Dual-build licensing note (phase 2, task 7): BicTerm ships in two build flavors.
- The default flavor (scheme
BicTerm, configurationsDebug/Release) links the Coder tailnet-tunnel Go core (CoderNet.xcframework, built fromCoderNet/on top of coder/coder v2.36.4). Because that core is AGPL-3.0, default-flavor binaries are AGPL-3.0 binaries — fine for open-source distribution. - The AppStore flavor (scheme
BicTerm-AppStore, configurationsAppStore-Debug/AppStore-Release, built viascripts/build-appstore.sh) contains no AGPL code at all: theCoderTunnelframework target and theCODER_TUNNELcompilation flag are excluded, coder connections use the direct-SSH path, and a three-layer audit (build-system / bundle+otool/stringssymbol sweep) proves the exclusion each release. SeeCoderNet/LICENSE-AGPL-NOTICE.mdandDocs/SECURITY.md.
This project was developed using AI-assisted code generation ("vibe coding"). The code was produced by autonomous AI agents executing a detailed plan. While it compiles, passes tests, and has been reviewed, it has not received the level of human scrutiny that hand-written production code would. Use at your own risk.