Expand description
Restore Apple Silicon Macs over USB — the engine behind the restorekit
CLI, usable directly from Rust (e.g. to build a GUI).
The workflow has four stages, each a module:
device— thedevice::Deviceprimitive: every connected Apple USB device with itsdevice::UsbModeand identity (chip/board/ECID/model). Enumerate withdevice::list, select withdevice::find/device::waitand adevice::Target, and fill in booted Macs’ ECIDs withdevice::identify.dfu— put a cabled target into DFU mode with the USB-PD trigger ([dfu::vdm], Apple Silicon macOS hosts only), and catch the Mac a trigger just put into DFU withdfu::watch(subscribe before triggering).firmware— resolve the correct IPSW for a model, download it resumably, verify its checksum, and cache it.restore— restore or revive the device via the statically-linkedidevicerestoreengine (DFU mode only).
§Design
The library never writes to stdout/stderr and never prompts. Every
long-running call takes a &mut dyn FnMut(Event) progress callback, so
a CLI can draw progress bars, a GUI can update a view, and tests can assert on
the event stream — all from the same code. Every fallible call returns
Result, a std::result::Result over Error.
§Example
Detect a Mac in DFU mode, download the right firmware, and restore it:
use std::time::Duration;
use restorekit::{device, firmware, restore, Event, Mode, Result};
fn main() -> Result<()> {
// Pick a target: the sole Mac in DFU mode (Target::Ecid picks one of
// several; device::list shows everything connected, in any mode — put a
// cabled target into DFU first with dfu::vdm::enter_dfu).
let dev = device::find(device::Target::One)?;
let identifier = dev.identifier().expect("known model");
let ecid = dev.ecid.expect("DFU devices always carry an ECID");
// Resolve and download the latest signed firmware into the cache.
let fw = firmware::resolve(identifier, None)?;
let cache = firmware::default_cache_dir()?;
let ipsw = firmware::download(&cache, &fw, &mut |event| {
if let Event::DownloadProgress { received, total } = event {
eprintln!("{received}/{total}");
}
})?;
// Erase-restore the device, printing each step.
restore::restore(&ipsw, ecid, Some(&cache), Mode::Erase, false, &mut |event| {
if let Event::RestoreStep { name, progress, .. } = event {
eprintln!("{name}: {:.0}%", progress * 100.0);
}
})?;
Ok(())
}Re-exports§
pub use device::Device;pub use device::Port;pub use device::Target;pub use device::UsbMode;pub use dfu::host_can_trigger_dfu;pub use dfu::manual_dfu_instructions;pub use error::Error;pub use error::Result;pub use firmware::Firmware;pub use progress::Event;pub use restore::Mode;