Spellwire is an iPhone-first remote control for Codex on macOS. It connects directly to your Mac over SSH on the local network or through Tailscale, keeps its view aligned with the same local Codex environment used by Codex.app, and does not depend on a relay or hosted control plane.
Status: alpha. This repo now includes a buildable TypeScript helper scaffold in
src/and a rudimentary interactive iPhone client inspellwire-ios/. Helper-owned Codex sync, recent-window thread opens with lazy older-history paging, Git status and commit flows, rollout recovery, terminal, files, and preview surfaces are present as early implementations and are not production-ready yet. The helper lifecycle scaffold uses a LaunchAgent on macOS and a detached background process on Linux for local development and CLI validation. Spellwire's supported host target for the product remains macOS in v1.
- An
iOS 26.4+SwiftUI app designed for iPhone first. - A direct SSH client for controlling a Mac that runs Codex locally.
- A local-first system that mirrors the same Codex universe already present on the Mac.
- A product that works on LAN and over Tailscale without introducing a relay.
- A shell-neutral SSH workflow that does not assume the Mac account uses a POSIX login shell.
- Browse multiple projects and multiple chats from the same Mac.
- Keep the full local Codex history visible on iPhone, with fast recent-window opens and lazy older-history loading for large chats.
- Reattach to the correct thread,
cwd, and runtime context instead of following only the thread currently open on the desktop. - View helper-owned Git working-tree counts and structured diffs for the selected thread
cwd, then commit, push toorigin, or open a GitHub pull request from the latest agent turn when supported.
- A real SSH PTY terminal on iPhone.
- Backed by a pinned
libghostty-vtrevision or vendored snapshot. - Built as a native terminal surface, not a simplified log viewer.
- A Finder-like remote file manager over SSH and SFTP.
- The current scaffold can browse, search, preview, export, edit text, create folders, and delete remote items.
- Rename, move, upload, and download are planned follow-on file operations rather than fully wired v1 behavior today.
- Broad file access without trying to become a second full desktop IDE.
- Target v1 preview transport is SSH port forwarding to localhost services on the Mac.
- The current iPhone scaffold supports manual preview setup through either a forwarded port or a direct browser URL on the host record.
- Helper-backed preview discovery exists in the helper scaffold, but it is not wired into the iPhone app yet.
Codex.app on the Mac remains the development environment. Spellwire stays in sync with that same local environment instead of creating a separate mobile backend or a separate remote state store.
The diagram below shows the intended v1 architecture. In the current repo scaffold, the helper already exposes spellwire previews list, but the iPhone preview browser still relies on manual host configuration and does not consume helper-backed preview discovery yet.
flowchart LR
subgraph iPhone["iPhone"]
IOS["Spellwire iOS app<br/>SwiftUI + Liquid Glass"]
CHAT["Codex workspace"]
TERM["Terminal UI"]
FILES["File browser"]
PREV["Preview browser"]
KEYS["Ed25519 key + pinned host fingerprint<br/>stored on device"]
end
subgraph Mac["macOS host"]
SSHD["sshd<br/>Remote Login"]
HELPER["spellwire helper<br/>LaunchAgent on macOS<br/>background process on Linux dev"]
RPC["spellwire rpc<br/>JSON over stdio"]
OPEN["spellwire open <threadId>"]
PRELIST["spellwire previews list"]
APPSERVER["codex app-server"]
SESS["~/.codex/sessions<br/>sessions + rollout artifacts"]
CODEXAPP["Codex.app"]
PTY["SSH PTY shell"]
SFTP["SFTP / SSH file ops"]
TUNNEL["SSH port forwarding"]
end
IOS --> CHAT
IOS --> TERM
IOS --> FILES
IOS --> PREV
IOS --> KEYS
IOS -->|"SSH exec + JSON RPC"| SSHD
IOS -->|"SSH PTY"| SSHD
IOS -->|"SFTP / SSH file ops"| SSHD
IOS -->|"SSH port forwarding"| SSHD
SSHD --> HELPER
SSHD --> PTY
SSHD --> SFTP
SSHD --> TUNNEL
HELPER --> RPC
HELPER --> OPEN
HELPER --> PRELIST
HELPER <-->|"attach / manage"| APPSERVER
HELPER <-->|"recovery / catch-up"| SESS
HELPER -->|"open explicit thread"| CODEXAPP
CHAT -->|"thread/list, thread/read, thread/resume,<br/>live updates"| HELPER
TERM -->|"interactive shell session"| PTY
FILES -->|"browse / preview / export / edit"| SFTP
PREV -->|"localhost preview access"| TUNNEL
Spellwire's helper-owned sync contract is:
- Use paginated
thread/listacross relevant source kinds to discover all projects and chats, including archived threads. - Use
thread/resumewhen opening or reattaching to a thread so the same thread id stays bound to the rightcwdand runtime context. - Use
thread/read(includeTurns=true)for canonical history hydration and reconciliation, with recent-window reads for fast initial opens and older-history paging when the user reaches the top of the loaded chat. - Use live notifications such as
thread/*,turn/*,item/*/delta, anditem/completedfor low-latency UI updates. - Use persisted rollout and session files in
~/.codex/sessionsas the recovery and catch-up path for running chats, context-window usage, off-screen runs, and desktop continuity. - Merge history item-aware, not
turnId-only. For huge or still-running chats, open with a recent window first, lazily page older history, and run canonical reconciliation afterward. - Keep the mobile app independent from whichever desktop thread is currently selected in
Codex.app. - Keep Git working-tree status, diff rendering data, and commit mutation flows as helper-owned adjunct state instead of folding them into
thread/read.
Because Codex.app may not live-refresh external writes, desktop handoff and refresh behavior are helper-owned and bounded. Mobile correctness must not depend on desktop route state.
The current helper scaffold exposes typed JSON RPC for thread-local Git status, structured diffs, commit previews, and commit execution. The iPhone chat can show a top-right diff pill for a dirty thread, open a helper-owned unified diff viewer, and attach Diff plus Commit & Push actions to the latest idle agent message.
Current v1 limits are intentionally narrow:
- Git counts and structured diffs are scoped to the current thread's file-change history, while commit actions stage and commit the full working tree
- pushes target
originonly - pull request creation is GitHub-only and requires authenticated
gh - the iPhone app consumes typed helper JSON and does not scrape Git CLI text
- A Mac running macOS with
Remote Loginenabled. - Codex installed and signed in on that Mac.
- Node.js for the helper install path.
- Reachability over local network or Tailscale.
- An iPhone running
iOS 26.4+. - Xcode if you are building the iPhone app from source.
For local development in this repo today:
npm install
npm run build
node dist/src/cli.js up
node dist/src/cli.js status
node dist/src/cli.js doctorThe intended public distribution path remains a globally installed npm package. The public command surface is:
npm install -g spellwire@latest
spellwire up
spellwire status
spellwire logs
spellwire doctorWhen the package is published, the update path stays:
npm install -g spellwire@latestPlanned public CLI contract:
spellwire upspellwire stopspellwire statusspellwire logsspellwire doctorspellwire rpcspellwire open <threadId>spellwire previews list
Homebrew is intentionally later work, not part of the initial required path.
Spellwire's SSH bootstrap commands are expected to work even when the remote account uses fish, zsh, bash, or another common shell. POSIX bootstrap logic should run through an explicit /bin/sh wrapper instead of assuming the login shell accepts POSIX script syntax directly.
v1 uses a manual SSH trust model. The current iPhone scaffold already generates and stores the Ed25519 key locally, shows the OpenSSH public key for authorized_keys, and requires host-fingerprint approval before connecting.
- Enable
Remote Loginon the Mac. - Confirm that Codex can run locally on the Mac and that
codex app-serveris available. - Install the Spellwire helper globally from npm once the package is published.
- Generate an Ed25519 keypair inside the iPhone app and store the private key in iOS secure storage.
- Add the iPhone public key to the Mac user's
~/.ssh/authorized_keys. - Verify and pin the Mac host fingerprint in the iPhone app.
- Enter host, user, and port details in the iPhone app.
- Connect over LAN hostname or IP, or over a Tailscale hostname or IP.
v1 does not assume QR bootstrap and does not depend on macOS Keychain.
The setup command shown for authorized_keys is intended to stay shell-neutral for common login shells on the Mac.
Today this repository includes:
- a buildable TypeScript helper scaffold under
src/withspellwire up|stop|status|logs|doctor|rpc|open <threadId>|previews list - macOS LaunchAgent helper lifecycle support plus a Linux detached-process fallback for local helper development and CLI validation
- helper tests under
test/for runtime paths, launch-agent generation, thread mapping, rollout recovery indexing, and Git working-tree RPC behavior spellwire-ios/with host onboarding, Ed25519 identity management, host fingerprint pinning, a Codex-first workspace, helper-owned Git diff and commit UI, and secondary terminal/file/preview surfaces- shared project assets under
.github/assets/ - docs that define the target SSH-first architecture
This repository does not yet contain a production-complete v1 runtime. The helper, sync layer, and iPhone experience are scaffolded and interactive, but they still need hardening, deeper recovery coverage, and more complete terminal/file/preview behavior.
- iPhone-first SwiftUI app
iOS 26.4+- Liquid Glass visual system
- macOS host only in v1
- LAN and Tailscale support
The iOS target reads its signing values from spellwire-ios/Config/Signing.xcconfig.
Create a local override before you build for a device or archive:
cp spellwire-ios/Config/Signing.local.xcconfig.example spellwire-ios/Config/Signing.local.xcconfigThen set your own values in Signing.local.xcconfig:
SPELLWIRE_BUNDLE_IDENTIFIERSPELLWIRE_DEVELOPMENT_TEAM
The local file is gitignored so personal signing data stays out of the public repo.
Open spellwire-ios/spellwire-ios.xcodeproj in Xcode and use the shared spellwire-ios scheme for local development.
Prefer simulator builds and runs first when validating routine iPhone app changes. Device builds and archives require the local signing override described above.
Spellwire is being built as an AGPL-3.0-only open-source project. Keep the repository docs and release artifacts consistent with that license policy.
The full license text lives in LICENSE.
