53 Commits
Author SHA1 Message Date
LakrandClaude Opus 5.5 602140a1d3 Format the tunnel network, hierarchy and patcher sources
swiftformat output only: wrapped single-line bodies, sorted imports, dropped
redundant self, throws and async, and plain numeric literals. No behavior
change; VPhoneCoreKitTests and the touched FirmwarePatcherTests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 18:52:12 +09:00
Yanni Pang c3d81c7ca2 feat: expose native AX parent-child hierarchy snapshots 2026-09-30 18:48:20 -04:00
LakrandClaude Opus 5.5 88120ff043 Tell the host the configured UDID, not only the profile check
The UDID override used to reach misagent and installd alone; Xcode, lockdown
and usbmuxd kept seeing the guest's own, so a paid team's profile could not
name the VM. The host reads the UDID in three places, and each is reachable
from userspace:

- lockdownd and remoted join vpIsMISFixTarget, so the spawn hooks insert
  libmisfix into them. Only the MobileGestalt interpose acts there
  (MISFixProcessOnlyNeedsIdentity keeps the MIS detours out). The hook now
  matches the obfuscated key remoted asks with, re6Zb+zwFKJNlkQTUeT+/w.
- MGCopyAnswerWithError takes three arguments; the hook declared two and
  crashed remoted, the first hooked caller of that spelling.
- vphoned sets the USB serial string, which is what usbmuxd names a device
  by (vphoned_usb.m, com.apple.private.usbdevice.setdescription, with
  AllowMultipleCreates), goes off the bus and back, and reapplies it at boot
  once the USB device exists.
- udid.set/clear SIGKILL the hooked daemons (remoted ignores SIGTERM) and
  always re-enumerate, which is also what relaunches remoted.

Measured on test-27.0: idevice_id, lockdown and the RSD handshake over both
transports report the override after udid.set and after a reboot, and the
guest's own after udid.clear.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 23:36:55 +09:00
LakrandClaude Opus 5.5 1c19bb41da Give libmisfix one route: the spawn hooks, SpringBoard included
SpringBoard did carry SystemHook; what it lacked was libmisfix beside it.
Which libraries a process gets is decided in its parent, and launchd starts
SpringBoard itself, so SystemHook's list naming it was never consulted. A
probe reading KERN_PROCARGS2 showed DYLD_INSERT_LIBRARIES held SystemHook
alone in SpringBoard and both libraries in installd and misagent.

  - vpIsMISFixTarget moves to InjectionEnvironment.h and the launchd hook
    asks it too, inserting libmisfix only when the dylib exists.
  - vpInsertHooks dropped the extra library when the environment already
    named SystemHook; fixed, with make test-injection-environment.
  - No guest binary carries a libmisfix load command. The three declarations
    that added them are gone, cfw install puts each .bak back and removes
    /mf, and inject-dylib --reclaim-source-version goes with its only caller.
  - SystemHook's logs are world-writable: a root-created 0644 file silently
    dropped every line from a mobile process, which is what made SpringBoard
    look as though it never carried the hook.

Measured on test-26.4 and test-27.0, both recreated from local IPSWs: from
the first boot SpringBoard, installd and misagent carry both libraries, and
AirBuild installed through installd opens on both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:46:10 +09:00
LakrandClaude Opus 5.5 7a413718d5 Let SpringBoard carry libmisfix so a signed app launches on 27.0
A paid team's IPA installed on test-27.0 and was refused at launch with
0xE8008026: SpringBoard asks MIS itself, and it never carried the hook. The
spawn route SystemHook-vphone.c listed it under was never reached.

  - system-springboard-cfw-launch_authorization links libmisfix into
    SpringBoard with a weak load command, as installd and misagent are.
  - SpringBoard's header has 16 spare bytes, so the command names a /mf
    root alias and inject-dylib --reclaim-source-version drops
    LC_SOURCE_VERSION to leave the re-signer room for its signature.
  - MISFixInstallPolicy now runs in installd only.

Measured on test-27.0 after a cold boot: SpringBoard loads libmisfix, MIS
returns 0x0 for AirBuild, and it launches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 22:18:21 +09:00
LakrandClaude Opus 5 b40c8e5b0e Let the keychain browser read, edit and delete an item
vphoned saw nothing through Security.framework, so every row the browser
showed came from the keychain database as encrypted metadata, and the
only delete path was the test item's account and service pair.

Give vphoned the "*" keychain access group, which securityd special
cases into every group, and name items by class, account, service,
server and group. An identity with no attribute at all is refused so a
query cannot widen into a whole class. keychain.get and keychain.update
now belong to GuestKeychain alone; the file tool copies passed "genp"
straight to secClass(named:), which never accepted it.

Accessible rows list as hidden rather than protected: the value is one
read away, not out of reach. The strings file also names the trackpad
menu item, which had no entry at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 20:12:13 +09:00
LakrandClaude Opus 5.5 4da0847af5 Let the VM window skip Setup Assistant on an activated guest
vphoned reports `setup_pending` in /v1/health and serves `setup.status`
and `setup.skip`. Device › Skip Setup Assistant… is enabled while the
guest reports it pending, and asks before it runs.

SpringBoard decides whether to run Setup once, when it starts. It does
while `SetupDone` in com.apple.purplebuddy is not true, and runs the flow
shown after a software update while `SetupVersion` is below
SetupAssistant.framework's BYBuddyIOSCurrentVersion (an int32, 11 on 26.4
and 27.0). Writing the keys while Setup is on screen does not dismiss it,
and killing Setup only makes SpringBoard start it again. So the skip
writes `SetupDone`, `SetupFinishedAllSteps` and `SetupVersion` through
cfprefsd for user mobile, then restarts SpringBoard. With every other key
in the domain removed, those three still reach the Home Screen, and no
later panes appear.

The experiments are in Research/Guest/setup_assistant_skip.md, including
how to send a guest back to Setup for testing. MCInstall's
SetCloudConfiguration, which pymobiledevice3, go-ios and cfgutil use,
works only on an erased device, so it does not fit here.

Verified on test-26.4 with this bundle: after deleting `SetupDone`,
Setup's hello screen appeared and setup.status reported pending true and
running true. setup.skip without force was refused. With force it
returned pending false, SpringBoard restarted and unlocked to the Home
Screen. Deleting `SetupVersion` then skipping also reached the Home
Screen. Afterwards the domain matched its state before the test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 19:36:11 +09:00
LakrandClaude Opus 5 ce7b42d21f Restart the MIS daemons when an environment update replaces their hook
libmisfix.dylib was already in the environment update's list on both
sides, but replacing the file changed nothing until something restarted
the daemons holding the old copy mapped, the same way the camera daemon
already needed restarting for libvcamcaptured. A new SystemHook counts
too, since that is what inserts libmisfix into them.

SpringBoard is deliberately not restarted. That is a respring, which is
`system.respring` to ask for, not something an environment update should
do behind the back of whoever is looking at the screen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 18:52:40 +09:00
LakrandClaude Opus 5 3cd176ea18 Restart installd too when the profile UDID changes
udid.set restarted misagent and deliberately left installd alone, on the
grounds that installd "asks misagent". It does not. A lockdown-path
install runs +[MICodeSigningVerifier
_validateSignatureAndCopyInfoForURL:withOptions:error:], which calls
MISValidateSignatureAndCopyInfo and evaluates the profile's
ProvisionedDevices itself. Two processes answer the UDID question
independently, which is why cfw install injects libmisfix into both, so
refreshing one and not the other leaves them disagreeing.

Measured on test-26.4: with the override changed to a UDID that is in
the profile and only misagent restarted,
_installEmbeddedProfilesWithError: passed and installd then failed the
same install with 0xE8008015, still holding the UDID from before the
change. installd had been up since boot (pid 281) while misagent had
been restarted 24 minutes later (pid 819).

Restarting installd can abort an install already in flight. That is the
lesser evil: changing the device's identity underneath a running install
is incoherent anyway, whereas leaving one of two evaluators on a stale
answer fails later, somewhere else, with an error that never mentions
the UDID.

This is necessary but, on its own, not sufficient — a hand-restarted
installd still refuses the same install. The remaining half is that
installd's UDID query may not reach this hook at all; see the next
commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 18:41:05 +09:00
LakrandClaude Opus 5.5 2aaafaf2ad Set the UDID misagent sees from the VM's Device menu
libmisfix answers misagent's UniqueDeviceID from /var/db/vphone/misfix.plist
(then /usr/lib/libmisfix.plist) and rereads it when it changes. vphoned now
serves udid.get, udid.set and udid.clear (capability udid_override): set
writes the data-volume plist atomically, keeping its other keys, reads it
back the way the hook resolves it, and sends SIGTERM to misagent so launchd
starts it fresh. installd is left alone and the guest is not rebooted. clear
removes the key but keeps the file, so a stale /usr/lib value cannot win.

The VM window's Device menu gets Set UDID… (prefilled, checks the 8-16 or
40 hex digit forms) and Reset UDID, enabled when the guest reports the
capability. Only misagent's profile check sees the value; Xcode, devicectl
and lockdown still see the guest's own UDID.

Also bumps Launchpad to 2.2.0.

Not yet verified on a running guest: that misagent's sandbox can read the
data-volume plist, and that launchd restarts misagent on the next check.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:22:16 +09:00
LakrandClaude Opus 5 8d7d914f52 Let Xcode install an app the guest did not get from Apple
Xcode could not install anything onto a guest unless it was signed with an
Apple leaf certificate, even though the guest runs unsigned code perfectly
well. Measured on a booted 26.6.2 guest: unsigned and ldid-shaped bundles
fail at 0xE800801C, a `codesign --sign -` bundle at 0xE8008014, all from
+[MICodeSigningVerifier _validateSignatureAndCopyInfoForURL:withOptions:error:].
A bundle pushed in through vphoned's apps.install, signed with nothing but
this project's own signature, installs and reaches the foreground — so the
kernel, lsd and SpringBoard already accept it and installd's check is the
only gate.

libmis accepts an ad-hoc signature outright when the caller passes the
AllowAdHocSigning option, which installd never does, and it fills the whole
info dictionary itself. So libmisfix.dylib interposes
MISValidateSignatureAndCopyInfo and adds the option, and cfw install attaches
it to installd with an LC_LOAD_WEAK_DYLIB. Nothing in the dyld shared cache
is touched, deliberately: writing a cache code page is what leaves a 27.0
guest unable to boot in #532.

The same dylib answers the other refusal. A paid team's profile fails at
0xE8008012 because the VM's UDID is in nobody's ProvisionedDevices, and only
a free personal team gets auto-registration. misagent obtains that UDID from
MGCopyAnswer -- its other source, an emulated UDID in the kernel's
codesigning configuration, is unreachable here: the sysctl is a four-byte
flags word and amfi_emulate_device_udid is in neither the kernelcache nor
TXM. Interposing MGCopyAnswer lets libmisfix.plist name a device the team has
already registered. Off until a UDID is set there.

What Xcode and lockdown report is unchanged and still the guest's own UDID;
TXM builds that one from the device tree before the kernel runs. The two
answers disagree on purpose, because agreeing would mean a re-restore for a
UDID that still could not match a real device's.

Also fixes #532's second defect: DyldSharedCacheMISTrustAuthPatcher now
recognises its own output, so a second cfw install reports alreadyPatched
instead of aborting the install.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 15:20:20 +09:00
LakrandClaude Opus 5 6d4ca34e76 Take touch2's normalized flag seriously, share the unclamped mapping
`input.touch2` accepted a `normalized` parameter and never read it, while
`input.touch` beside it honours one. `vp_hid_touch2` takes 0..1 only, so a
caller passing screen points the way `input.touch` allows would have had both
fingers read as fractions and land in the corner, silently. Refuse the flag
instead.

The view's `normalizeCoordinate(allowOutside:)` had reimplemented
`VPhoneDisplayGeometry.normalizedPoint` minus the clamp, leaving the mapping
in two places while only one of them is tested. The geometry type takes a
`clamped` parameter now, with a test for the path the scroll gesture uses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-30 15:15:30 +09:00
lgzcoollg 5922ef1181 Replay trackpad gestures and Esc as guest touches (#533)
Two ways to drive the guest from the host, both landing on the same touch injection.

Trackpad: a two-finger scroll becomes a synthetic finger that presses under the pointer and follows the physical direction, with edge re-anchoring so a long swipe is not bounded by where the pointer sits, and a Home-strip snap so an unlock gesture starts inside the indicator strip. A pinch becomes two fingers spreading or closing around the pointer. The two never interleave.

Esc: iOS has no back key, and a forwarded Escape only reads as cancel (or stop-loading in Safari), so Esc and a new Device -> Back item replay the system back gesture instead. It is taken in VPhoneApplication.sendEvent, before the menu lookup, because AppKit does not reliably match a modifier-less Esc key equivalent.

Both land on the view existing touch path, so the guest-side half is shared: VPhoneGuestControl gains supportsMultiTouch and sendTouch2 for the new input.touch2 request, and VPhoneDaemon builds the two-finger hand event itself because icli carries one digitizer point per event and cannot express a pinch. A guest that predates touch2 degrades to a one-finger move.
2026-09-30 15:12:48 +09:00
LakrandClaude Opus 5.5 bc6d007d8b Turn the window with the guest's interface orientation
vphoned gains display.orientation, which asks SpringBoard's
activeInterfaceOrientation through AXSpringBoardServer instead of
capturing the screen, so the host can poll it every second. Guests that
report display_orientation are polled; others stay portrait.

The window's content view now holds the VM view turned to that
orientation. The VM view keeps its portrait bounds, so touch mapping is
unchanged: AppKit's conversion undoes the rotation. A windowed VM swaps
its sides around its center and shrinks to fit the screen; full screen
letterboxes the turned panel. A frame saved while sideways is turned
back to portrait at launch.

The Device menu gains Rotate Left (⌘←), Rotate Right (⌘→) and an
Orientation submenu checked by the current orientation, enabled only
while the agent reports display, so the keys otherwise reach the guest.
An orientation the front app refuses is skipped by the rotate keys and
reported by the submenu. New strings are translated for ja, ko, vi and
zh-Hans.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:39:22 +09:00
LakrandClaude Sonnet 5.5 090df08195 Reword user-facing errors, app info and Launchpad copy in plain written English
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 23:35:59 +09:00
LakrandClaude Opus 5.5 6e1b0cfe41 Save files dropped on the window to the Files app
Dropping files on the VM window used to take only the first .ipa or
.tipa. Every dropped file now goes through vphoned: a package is
installed as before, and any other file is uploaded and moved into the
Files app's On My iPhone › vphone-drop. Folders are not taken, and one
alert sums up the drop.

On My iPhone is `File Provider Storage` in the
group.com.apple.FileProvider.LocalStorage app group, whose container
UUID differs per device, so vphoned finds it by the container metadata
(files.save_to_files_app, capability files_app_drop). The Files app shows
an item placed there at once when it looks like one the app creates:
owned by mobile, folders 755, files 644, no extended attributes, which
is what a folder made in the Files app has. A name already taken is kept
and the new file is numbered, as in "notes 2.txt".

Tested on a RootHide guest (iOS 26.6.2): a screenshot dropped on the
window and two files saved under one name through the RPC appear in
Files as vphone-drop with three items.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 23:13:35 +09:00
LakrandClaude Opus 5.5 2b253b6eeb Show the iOS version and guest address as the window subtitle
Once vphoned connects, the VM window's subtitle reads
`iOS <version> - <address>`, and it is empty while vphoned is away.

vphoned's health report took the first en* address, which sorts to en0's
fe80:: link-local address. It now picks a 192.x IPv4 address first, then
any other IPv4 that is not loopback or 169.254, then a routable IPv6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:43:59 +09:00
LakrandClaude Opus 5.5 9d3551cfa2 Load the bootstrap's LaunchDaemons from vphoned, as RootHide does
The launchd hook used to add the bootstrap's Library/LaunchDaemons to
launchd's cache under /System/Library/LaunchDaemons/vphone.<name> keys. A
job imported that way does not match the plist path a package script boots
out, so upgrades could not unload their own daemons. RootHide's own hook
loads only basebin/LaunchDaemons and leaves the rest to `jbctl startup`.

The hook no longer interposes xpc_dictionary_get_value. After boot vphoned
loads each plist in <root>/Library/LaunchDaemons under its real path. For
RootHide it first rewrites the plist on disk the way RootHide's launchctl
does (plistpatch.m, ported to Swift): Program, ProgramArguments[0], the
working and root directories, standard streams, watch and queue paths,
HOME/TMPDIR, KeepAlive.PathState, socket paths and fsevents paths get the
root prepended, and __Patched marks the plist. services.load patches a
plist under the root the same way, so no launchctl binary is needed.

vphoned installs RootHide under one fixed name,
.jbroot-000114514191980C, and the hook looks only there and in /var/jb.
A RootHide root under another name must be uninstalled first.

Tested on a RootHide guest (iOS 26.6.2): every daemon plist carries
__Patched and is registered under its own path, ssh and sudo work, and
launchctl bootout/bootstrap of ighostvtd and a dpkg install, remove and
reinstall of openssh-server all load the job again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 22:43:59 +09:00
LakrandClaude Opus 5.5 0f9a949bd6 Fix RootHide /dev, sshd and sudo loader links (#519, #520)
#519: vphoned wrote jbroot/dev as the link text /rootfs/dev. The kernel
resolves link text, not vroot paths, so it dangled: every shell failed on
/dev/null and sshd never started. It is now /dev, an existing
/rootfs/dev link is replaced, and rootfs -> / is created.

#520: Irisin unpacks packages without RootHide dpkg's hook, so nothing
linked .jbroot in deeper package directories, and sudo could not load
libsudo_util from usr/libexec/sudo.
- vphoned walks the bootstrap for directories holding Mach-O files and
  links each one, at install, at startup, and one second after the root's
  Library/dpkg changes. That pass also runs the base steps that waited for
  pwd_mkdb or ssh-keygen, so sshd has host keys once openssh is installed.
- The spawn hooks follow the executable's LC_RPATH and LC_LOAD_DYLIB
  entries inside the root and link each dependency's directory, within a
  fixed bound. SystemHook does the same for TweakLoader before its dlopen.
- launchd starts xpcproxy and bootstrap daemons through posix_spawnp, which
  the launchd hook now interposes. SystemHook is chain-loaded into every
  child whatever its environment; DISABLE_TWEAKS and safe mode only keep
  ElleKit out.

The installer moves to Daemon/Bootstrap, with RootHide and rootless code
in their own folders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 20:26:03 +09:00
LakrandClaude Opus 5.5 1df6c43cbc Let the host close vphoned connections so replies are not truncated
XNU's vsock answers close() and shutdown() with an immediate RESET or
SHUTDOWN and drops bytes still queued for host credit, which cut replies
of about 8 to 16 KiB. After its last write vphoned now waits for the host
to close (30 s fallback), echoes a WebSocket close instead of closing, and
the host HTTP client closes its side once it has read the reply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:14:30 +09:00
LakrandClaude Opus 5.5 235272673a Keep only the camera from EXP and drop the location app hook
Issue #438: guests built after the former EXP patches joined the JB flow
lost location. standard is now the JB baseline plus the virtual camera.

- watchdogd.hv_vmm_cache joins the hv_vmm_present concealment group and
  loses bootEssential: watchdogd only panics once the OID is renamed.
- The eight DeviceTree identity rewrites and the Preboot DeviceTree
  rewrite, newly declared as preboot_devicetree.identity, are blocked in
  standard. cfw install now asks the plan before the Preboot rewrite.
- Camera DT nodes, cam offsets and camera_dsc stay on.
- libvlocation.dylib and its SystemHook load are removed. location.set,
  clear and current call IcliKit directly again, which confirms a request
  by reading back a fresh, software-simulated fix at that coordinate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:12:15 +09:00
LakrandClaude Opus 5.5 b21910e01b Apply the formatter to the daemon, the VM, the IPSW cache and the host policy
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 21:02:50 +09:00
LakrandClaude Opus 5.5 e3c0d3f1c9 Post Darwin notifications from the Controls panel (#517)
* Post Darwin notifications from the Controls panel

vphoned gains notify.post {name, state?} and notify.state {name}, thin
calls into IcliKit 0.7.0's postDarwinNotification and
darwinNotificationState. The state is a full UInt64, so it is accepted
as a decimal string as well as a number.

The Controls panel gets a Darwin Notification section: a name field with
presets, an optional state, and Post and Read State buttons. This
replaces #248, which targeted the removed ObjC daemon and sources/ tree.

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

* Refuse a JSON boolean as a notification state

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 16:20:45 +09:00
xin b3888c7241 Seed RootHide PAM loader link 2026-09-28 15:59:08 +09:00
bf61a9eb3a Seed the RootHide bootstrap base from vphoned
Irisin's RootHide bootstrap unpacks packages, but nothing created what a
jailbreak's bootstrap installer normally does. iGhostVT hung on a missing
/tmp, /dev/null was unreachable under vroot, every getpw* lookup failed
with EINVAL, and sshd reset connections for lack of host keys.

ensureRootHideBase runs beside ensureRootHideLinks at install and on
startup. It creates tmp, var/tmp, dev, var/root and the account files,
builds pwd.db and spwd.db with the bootstrap's pwd_mkdb, and runs
ssh-keygen -A once openssh is installed. Existing items are left alone,
and the databases are rebuilt only when older than master.passwd, so a
changed password survives. Steps whose tool is not unpacked yet are
reported as deferred and run on the next start.

Co-Authored-By: McNight <mcnight@mcnight.fr>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 15:59:08 +09:00
Lakr 4fc4f1b115 Deliver VM location through guest app hook
Publish validated location state to authorized CoreLocation clients, keep guest hooks in sync, and add a Release artifact workflow for unsigned CI packages.
2026-09-25 20:08:40 +09:00
fbdbe21968 Harden host root, guest and bundle trust boundaries
A repo-wide security review found issues in the root CFW install, the
Launchpad helper, VM bundle handling and the guest boundary. This fixes
them and applies swift format.

- cfw install mounts guest volumes nosuid,nodev,nobrowse in a root-only
  temp folder and does every guest read and write through an open folder
  handle, never following links. BuildManifest cryptex paths must stay in
  the restore folder, and only a private copy is attached.
- Root no longer chowns or chmods the whole VM folder after an install.
  The shared walk skips hard links, symlinks, special files and other
  volumes.
- Every Launchpad helper action needs administrator authorization. Only
  the user who started a CFW install can cancel it. The helper refuses
  setuid, hard-linked, special or escaping-symlink entries in a bundle.
- Manifest file names must be single names in the bundle and point to
  regular files. vm import refuses links that leave the bundle.
- Guest file names from the file browser, QuickLook, drag-out and crash
  logs are validated and written exclusively, without overwriting, and
  are quarantined.
- The guest HTTP client no longer traps on a bare Content-Length, caps
  bodies and enforces a per-request deadline.
- The --api-listen proxy needs a per-launch token. vphoned refuses
  browser-origin and non-local Host requests.
- vphoned stops following links when it sets up Irisin and the camera
  files.

Thanks to fresh-fx59 for reporting the guest file name, HTTP parsing,
cfw install and manifest path issues in #469.

Co-authored-by: Aleksey Aksenov <5788874+fresh-fx59@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 17:30:11 +07:00
Lakr233andClaude Opus 5.5 7813b87998 Fix the remaining Swift, Objective-C and linker warnings
- vphoned: add the WebSocket handlers through syncOperations on the
  event loop, so the non-Sendable frame aggregator never crosses a
  concurrency boundary.
- Kernel patchers: drop `try` on the non-throwing parseMachO(), and log
  the recovered csflags reload in the cred label patch. No patch bytes
  change.
- Custom firmware installer: discard the deferred tool result explicitly.
- Video file producer: load the video track once with loadTracks.
- vcamcaptured: mask the class address as a plain integer.
- GPU compiler plugin: name its 18 runtime-resolved LLVM/MTL symbols with
  -U instead of -undefined dynamic_lookup. The import table is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:08:46 +07:00
Lakr233andClaude Opus 5.5 fc57dca56b Load the camera hooks without a bootstrap and add the environment update
The camera hooks were built and shipped but never reached the guest, so
Camera.app could not show a streamed frame. cfw install now places
libvcamcaptured.dylib and libcamfix.dylib in /usr/lib beside the launchd
hook and SystemHook. SystemHook treats /usr/libexec/cameracaptured as an
injection target and loads the daemon hook there, and loads libcamfix into
app processes that have AVFoundation loaded. Both hooks install their own
Objective-C method replacements, so neither needs ElleKit or a bootstrap.

A running guest gets changed copies of those four libraries through the
new vphoned environment update. environment.status reports the SHA-256 of
each library in /usr/lib; environment.install checks the uploaded copies,
remounts / read-write when needed, renames each library into place and
remounts / read-only again, because jailbreak detection reads a writable
root as rootful. It stops cameracaptured when a camera hook or SystemHook
changed and reports when the launchd hook needs a guest restart. The VM
process runs the update once per connection, after the vphoned
self-update, uploading only libraries whose hashes differ.

Not yet verified in a guest; the validation steps are in
Research/0_binary_patch_comparison.md and Research/vphoned_http_api.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:57:12 +07:00
Lakr 12f93e323e Merge bootstrap installation updates 2026-09-25 14:49:26 +09:00
Lakr 153cfeb58b Support local Irisin bootstrap and multi-root uninstall 2026-09-25 14:48:42 +09:00
Lakr 4d94fa3b58 Merge vphoned spawn proxy 2026-09-25 14:44:53 +09:00
Lakr 69c95170a8 Keep vphoned IO outside launchd Jetsam limit
Run a small launchd-owned proxy that posix_spawns the same signed binary in --io mode, reaps/restarts it, and exits the worker when the proxy dies. Preserve cached guest updates with a bounded streaming hash.
2026-09-25 14:41:54 +09:00
Lakr 158d704bc4 Store bootstrap completion on writable data volume 2026-09-25 14:37:51 +09:00
Lakr 3995e2c385 Seed RootHide loader links for bootstrap tools 2026-09-25 04:16:05 +09:00
Lakr 59873ece15 Add bootstrap uninstall and guest reboot flow 2026-09-25 04:01:19 +09:00
Lakr233 d032989027 Merge remote-tracking branch 'origin/main' into vphoned-panels
# Conflicts:
#	VPhoneExecutable/VPhoneVirtualization/Resources/Localizable.xcstrings
#	VPhoneExecutable/VPhoneVirtualization/UI/UserInterface/Menu/VPhoneMenuConnect.swift
2026-09-25 01:16:41 +07:00
Lakr233andClaude Opus 5.5 dd9acb1617 Add guest inspection panels and rebuild the menu bar
Seven new guest windows sit on the expanded vphoned API, each a SwiftUI
Table or Form in the Guest Tools style with its own model:

- Device Info: device, hardware, power, display, security, jailbreak
  environment, agent and a network interface table.
- Processes: sortable process table with memory, CPU time and jetsam
  bands, and signals behind a confirmation.
- Console: the guest unified log, captured back to back, with level and
  process filters, search, pause and save.
- Crash Logs: CrashReporter and DiagnosticReports with an .ips header
  summary, re-indented body, copy and export.
- Services: launchd services with state, last exit and launchd's own
  description; start, stop, restart, enable, disable, signal, remove.
- UI Inspector: accessibility elements and OCR text drawn over a guest
  screenshot, with hit-testing and tap.
- Controls: brightness, rotation, volume, low power, hardware buttons
  and keyboard input.

The App Browser gains an App Info inspector (bundle, entitlements, URL
schemes, data container, network policy), uninstall, launch, terminate,
install and open URL, and can open the File Browser at a data container.

The menu bar is now vphone, Edit, Device, Apps, Guest, Diagnostics,
Capture, Window. Device merges Keys with the device overrides and opens
Controls; Guest holds files, Keychain, clipboard and preferences;
Diagnostics is a top-level menu for the inspection windows with ⌥⌘
shortcuts and the Developer Mode, ping and agent hash checks. Panel
items are enabled from the connected agent's capabilities.

VPhoneGuestControl is observable, so every window follows the
connection state, and its probe writes only changed values. vphoned adds
memory.pressure, the three memory sysctls without jetsam's priority list,
for panels that poll. New strings are translated into Simplified
Chinese, Japanese, Korean and Vietnamese, including the %arg twins
Xcode 27 extracts beside existing printf-style keys.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 01:13:38 +07:00
Lakr233andClaude Opus 5.5 ce3e8fc891 Expose the rest of icli through vphoned
vphoned wired about a third of IcliKit. It now also answers device,
display, audio, network, gesture and text input, accessibility and OCR,
process, jetsam, launchd, unified log, crash report, packet capture, app
detail, system control, file tool, keychain and read-only package
methods. Each area lives in its own GuestAPI+Area.swift and is reached
from the default branch of the original switch.

processes.list joins the kernel process list with resource usage from
proc_pid_rusage, jetsam bands and RunningBoard bundle identifiers.

Operations that end a process, stop or remove a service, uninstall or
unregister an app, or restart the guest require force: true, so a stray
request cannot run them. icli errors now reach the caller as their own
code and message. The host client exposes call(_:params:) for panels,
and a shared panel helper file holds JSON readers and formatters.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 00:51:44 +07:00
Lakr d308fb9956 Record vphone firmware in bootstrap dpkg status 2026-09-25 02:37:38 +09:00
Lakr bd0720ab60 Show Irisin bootstrap progress in Guest menu 2026-09-25 02:28:11 +09:00
Lakr a150aaa87f Install Irisin bootstrap through vphoned API 2026-09-25 02:23:12 +09:00
Lakr233andClaude Opus 5.5 b7ceef03b9 Localize Guest Tools and let menu shortcuts win over the VM view
Guest Tools strings use String(localized:) against the VPhone bundle, with
String.LocalizationValue interpolation for formatted text. The catalog
gains the 71 new keys with zh-Hans, ja, ko and vi translations, inserted
in place so existing entries are unchanged.

capturesSystemKeys let the VM view take every key press before the menu
bar saw it, so menu shortcuts such as ⇧⌘F and ⌘W reached the guest
instead. A local key monitor on the VM window now offers each key press
to the main menu first and passes it to the guest only when no enabled
menu item handles it. This replaces the view's performKeyEquivalent
override, which never ran while system keys were captured.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-25 00:00:07 +07:00
Lakr f7a4a279e4 Localize VPhone interface and format pending changes 2026-09-25 01:48:27 +09:00
Lakr deb8004649 Drain tunnel writes and bound interrupted uploads 2026-09-25 01:14:19 +09:00
Lakr 85a6ccc645 Expose jailbreak layout and guest port tunnels 2026-09-25 01:03:13 +09:00
Lakr a624c632ec Use IcliKit for guest app installation 2026-09-25 00:34:39 +09:00
Lakr 9e7d820412 Use IcliKit 0.6.5 for guest Low Power Mode 2026-09-25 00:22:19 +09:00
Lakr 0e8946f4c9 Use IcliKit 0.6.4 for guest app foreground state 2026-09-25 00:07:14 +09:00
Lakr 0394ee6a0f Repair vphone guest controls, camera transport, and IcliKit integration 2026-09-24 23:57:46 +09:00