Skip to content

pi/hitl: Mac mini iOS+C6 HITL bench — darwin runner, headless signing, sshd lockout fix - #189

Merged
fughilli merged 1 commit into
mainfrom
claude/mac-ios-bench-deploy
Sep 25, 2026
Merged

fughilli merged 1 commit into
mainfrom
claude/mac-ios-bench-deploy

Conversation

@fughilli

@fughilli fughilli commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

Stands up the reservation daemon on a Mac mini for the iOS+C6 bench (Phase D). Rebuilt clean off current main (the WiFi fix #190 + screen-off #191 that this branch previously overlapped are already merged).

  • Pin hitl-reserve → 085532e5 (hitl-reserve#7, the --runner darwin backend) in both consumers: MODULE.bazel + pi/hitl/flake.nix (+ flake.lock, re-locked via nix).
  • hitl-darwin.nix — the additive nix-darwin module (launchd hitl-reserved --runner darwin on :8087, scoped sshd user, iOS toolbox). CRITICAL sshd fix: PermitUserEnvironment is now GLOBAL, not inside the Match block — sshd rejects it within a Match and then fails to parse the whole config, dropping every connection before the banner (a full ssh lockout). Validated with sshd -t. Documents the headless code-signing secret seam (HITL_SIGN_* dedicated keychain + HITL_ASC_* App Store Connect API key, injected via the daemon user's root-owned ~/.ssh/environment, never in-repo).
  • catalog-mac.json — one ios-phone unit (iPhone + its own C6), filled from the live Mac.
  • ios_build_server.py device-build — gated preamble unlocks the dedicated signing keychain and, for a paid team, passes the ASC API key to xcodebuild -allowProvisioningUpdates so the dev cert + profile are created/renewed fully headless. No-op for local GUI dev.

Verified live: nix-darwin system builds; daemon advertises ios-phone on :8087; C6 flashes host-native; end-to-end signed device build SUCCEEDS via the ASC API key (dev profile auto-created + iPhone registered). Remaining for a green on-device journey is the phone staying unlocked (DDI mount) — the harness path is proven.

HITL flake on the prior run was the usual netstack flake (video_stream/map_upload/led_capture 1/2), orthogonal to these Mac-only changes.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-25 04:03 UTC

…, sshd fix

Stands up the reservation daemon on a Mac mini for the iOS+C6 bench (Phase D):
a nix-darwin module (hitl-darwin.nix) that adds a launchd `hitl-reserved --runner
darwin` on :8087, a scoped sshd user, and the iOS toolbox — no container; each
reservation is a scoped SSH grant into the shared `hitl` user, driving the iPhone
(devicectl/usbmux) and the C6 (esptool over /dev/cu.usbmodem*) host-native.

  * Pin hitl-reserve -> 085532e5 (hitl-reserve#7, the --runner darwin backend) in
    both consumers: MODULE.bazel (@hitl_reserve) + pi/hitl/flake.nix (+ flake.lock).
  * hitl-darwin.nix: the additive module. CRITICAL sshd fix — PermitUserEnvironment
    is GLOBAL, not inside the Match block: sshd rejects that directive within a
    Match and then fails to parse the whole config, dropping every connection
    before the banner (a full ssh lockout). It's the one directive that can't be
    scoped; validated with `sshd -t`. Documents the headless code-signing secret
    seam (HITL_SIGN_* dedicated keychain + HITL_ASC_* App Store Connect API key,
    injected via the daemon user's root-owned ~/.ssh/environment, never in-repo).
  * catalog-mac.json: one ios-phone unit (iPhone + its own C6), hardware filled
    from the live Mac (C6 port /dev/cu.usbmodem*, iPhone UDID); the sim/second-C6
    unit dropped until a second C6 is attached.
  * ios_build_server.py device-build: gated preamble unlocks the dedicated signing
    keychain (HITL_SIGN_*) and, for a paid team, passes the App Store Connect API
    key (HITL_ASC_*) to xcodebuild -allowProvisioningUpdates so the dev cert +
    provisioning profile are created/renewed FULLY HEADLESS. No-op for local GUI
    dev (vars unset).

Verified on the live Mac: nix-darwin system builds; daemon advertises the
ios-phone unit on :8087; the C6 flashes host-native (esptool flash_id);
end-to-end signed device build SUCCEEDS via the ASC API key (dev profile
auto-created + iPhone registered). Remaining for a green on-device journey is the
phone staying unlocked (DDI mount) — tracked separately; the harness path is proven.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@fughilli
fughilli force-pushed the claude/mac-ios-bench-deploy branch from 8530762 to 9fe0359 Compare September 25, 2026 03:35
@fughilli fughilli changed the title pi/hitl: Mac mini iOS+C6 HITL bench — pin darwin runner + fill catalog pi/hitl: Mac mini iOS+C6 HITL bench — darwin runner, headless signing, sshd lockout fix Sep 25, 2026
@fughilli
fughilli merged commit 84a6dc6 into main Sep 25, 2026
10 of 11 checks passed
fughilli pushed a commit that referenced this pull request Oct 3, 2026
…discovery

The Mac iOS bench (#189) stood up the reservation daemon, signing and a bare
app install, but the app-driver JOURNEY lane could not actually run on a real
iPhone: a native Capacitor WKWebView loads the bundled web/dist at
capacitor://localhost/ with no query string, so the `?driver=…&ble=real` seam
(web/src/ui/app/main.ts) — which starts the journeys — was unreachable. And the
iOS client posted to a build server that doesn't exist (:8765 /device-*?url=)
instead of the real tools/ios_build_server.py (:8099 /run?task=…).

Drive the iPhone the SAME way as Android by pointing Capacitor's server.url at
the station-served app URL (which carries the query) so the WKWebView loads it
over the LAN and enters driver mode; native BLE keeps bridging through the
Capacitor Improv plugin.

- web/capacitor.config.ts: set server.url from CAP_SERVER_URL when present
  (unset = load the bundle, as before).
- tools/ios_build_server.py: accept a `server_url` param on /run and pass it as
  CAP_SERVER_URL into the task env so `cap sync` bakes it (env only, never argv;
  validated to an http(s) URL).
- pi/hitl/phone/phone_target.py: IosBuildClient now hits the real /run?task=
  endpoint on :8099; the iOS targets thread the app URL through server_url on a
  cap-sync→build→install→launch chain (not a devicectl launch url=, which can't
  open a URL on a real device). Default bundle id fixed to dev.splanc.app.
- pi/hitl/phone/launcher.py: serve the app on 0.0.0.0 so an off-box phone can
  fetch it (+ SO_REUSEADDR); the returned base stays loopback for the browser lane.
- web/ios-config/apply.sh: NSAllowsLocalNetworking so ATS permits cleartext to
  the LAN station + device (narrower than arbitrary loads; App-Store-acceptable).

Dynamic C6 serial-port discovery (owner requirement — never hardcode the
renumbering /dev/cu.usbmodem*): pi/hitl/harness/serial_discovery.py resolves the
port from the stable USB identity (Espressif VID 0x303a), matching the reserved
board by its HITL_ADAPTER_SERIAL when several C6s are attached. Reusable on the
Mac bench and the Linux rigs; a py_binary CLI prints the path for
`esptool --port "$(…)"`. Pure selection logic is dependency-injected and
unit-tested without pyserial/hardware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

This branch had an error being deployed

1 failed deployment
HITL — 9fe0359a Deployed Sep 25, 2026 by fughilli via hitl_tests #549
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants