Documentation
Device install & signing
walnut device builds for a physical iPhone, signs with Automatic signing, installs via devicectl, and launches the app. Pair with walnut debug --attach over Wi‑Fi once the debug server is up.
Simulator stays on walnut simulator. This page is the on-device path.
One-minute path
# Sign in to the Apple ID you want in Xcode, then pick Team (saved to walnut.json)
walnut device login
# See paired phones / known teams
walnut device --list
walnut device --list-teams
# Build + sign + install + launch
walnut device
walnut device --device "f" --debug
# In-process hot reload on device (same as simulator --watch)
walnut device --watch --device f --host <phone-lan-ip>
Then on the Mac (or rely on --watch pushing reloads itself):
walnut debug --attach --host <phone-lan-ip>
--watch implies --hot + --debug: after cold install, edits recompile a device dylib, devicectl copy into Documents, and TCP reload. See hot-reload.md.
Login flow (another Apple ID / team)
Apple does not allow a CLI to collect Apple ID + password (2FA). The Walnut flow is:
- Xcode → Settings → Accounts — add / switch to the Apple ID that owns the profile you want
walnut device login [dir]— pick Apple ID and Team separately (and optionally an App Store Connect API.p8key)- Values are written into
walnut.jsonand reused by laterwalnut deviceruns
Xcode often caches teams under only one signed-in email even when Accounts lists two.
walnut device --list-teams therefore shows:
- Apple IDs from Xcode + the macOS Accounts database (so a second ID like
[email protected]appears) - Teams by Team ID (signing identity); the “Xcode-linked email” column is a hint and can be wrong
walnut device login
# → pick Apple ID (or type the email)
# → pick Team ID
# → optional ASC API key for headless auth when the Xcode session is stale
# Install onto a specific phone (required when several are online)
walnut device --device f
login only saves signing identity — it does not pick a phone. --device belongs on the install command. Device pairing is independent of which Apple ID is signed into Xcode.
Filter without re-running login:
walnut device --account [email protected]
walnut device --team X57Z768URP
App Store Connect API key (headless)
When Xcode says Unable to log in, or for walnut testflight, use a Team API key
(not an Individual key — those fail export with ASC -20101 / a misleading password error):
walnut.json / env | Meaning |
|---|---|
authenticationKeyPath / WALNUT_ASC_KEY_PATH | Path to AuthKey_….p8 |
authenticationKeyID / WALNUT_ASC_KEY_ID | Key ID |
authenticationKeyIssuerID / WALNUT_ASC_ISSUER_ID | Issuer ID (UUID at top of the Keys page) |
Create under App Store Connect → Users and Access → Integrations → App Store Connect API → Team Keys.
walnut device login stores these; xcodebuild then gets -authenticationKeyPath / -ID / -IssuerID.
See testflight.md.
Prerequisites
| Need | Notes |
|---|---|
| Xcode + Command Line Tools | xcrun devicectl / xcodebuild |
xcodegen | brew install xcodegen |
| Paired iPhone | Finder or Xcode; unlock; Developer Mode on |
| Apple ID / Team | Personal or paid team; use login to pick |
Team ID resolution
First match wins:
--team YOURTEAMID- Env
WALNUT_DEVELOPMENT_TEAMorDEVELOPMENT_TEAM walnut.json→developmentTeam(fromlogin)--accountfilter over Xcode teams- Interactive picker (TTY)
- First discovered team
Example walnut.json after login:
{
"name": "Todo",
"bundleId": "dev.walnut.todo",
"sources": "src",
"runtime": "$(brew --prefix)/share/walnut/runtime",
"developmentTeam": "X57Z768URP",
"developmentAccount": "[email protected]"
}
What the command does
- Typecheck
src/ - Compile LLVM →
Main.owith device triplearm64-apple-ios16.0(not simulator) xcodegen+xcodebuildforgeneric/platform=iOSwithCODE_SIGN_STYLE=Automaticand-allowProvisioningUpdatesxcrun devicectl device install app --device …xcrun devicectl device process launch --terminate-existing …
--debug sets DEVICECTL_CHILD_WALNUT_DEBUG=1 so the in-app debug server listens (same as Simulator’s SIMCTL_CHILD_*).
Limits
- No on-device hot reload yet — use Simulator
--hot --watchfor that loop - Personal teams re-sign often; first install may prompt Trust on the phone
- Bundle IDs must be unique under your team if you collide with another app
- watchOS / multi-destination builds are out of scope
- CLI cannot replace Xcode’s Apple ID password UI —
loginonly selects an already-signed-in account / team (or ASC API key)
Related
- app-store.md — publish through App Review + release
- testflight.md — Release archive + App Store Connect / TestFlight upload
- debug.md — Wi‑Fi attach after install
- toolchain.md — full CLI table
- hot-reload.md — Simulator / device
--watchreload