-
Notifications
You must be signed in to change notification settings - Fork 0
Deployment
How BootstrapMate gets onto a Mac and how it gets invoked. Read this when you are building the enrollment package for a new fleet, or when you need to make an already-provisioned Mac run the bootstrap again.
BootstrapMate ships as a single flat installer package with the identifier
com.github.bootstrapmate, named BootstrapMate-<version>.pkg. Its payload is one
non-relocatable app bundle:
/Applications/Utilities/BootstrapMate.app
Inside that bundle are the three executables. Contents/MacOS/managedbootstrapinstall is the
command-line tool that does all the provisioning work; Contents/MacOS/BootstrapMateGUI is
the SwiftUI app; Contents/MacOS/BootstrapMateHelper is the privileged XPC helper used only
by the GUI.
The package built by this repository's public release workflow is unsigned and un-notarized. Sign, notarize and staple it in your own pipeline before deploying it — see Building and Signing.
The package's postinstall script runs as root and, in order:
- Creates
/Library/Managed Bootstrap/logsand/Library/Managed Bootstrap/cache, mode755. It does this before the daemon can start, so the first run always has somewhere to log. - Creates the symlink
/usr/local/bootstrapmate/managedbootstrapinstallpointing at the CLI inside the app bundle, and removes the legacyinstallapplicationssymlink from earlier versions. - Sets
/Library/LaunchDaemons/com.github.bootstrapmate.plistto644 root:wheel, runslaunchctl bootout system/com.github.bootstrapmateto clear any job already loaded under that label, thenlaunchctl load -won the plist. The bootout matters on upgrade:loadno-ops when the label is already loaded, which would leave launchd pointing at the previous executable path. - Copies the helper's plist out of the bundle to
/Library/LaunchDaemons/com.github.bootstrapmate.helper.plist, rewrites itsBundleProgramkey into an absoluteProgramArgumentsentry, and runslaunchctl bootstrap systemon it.
Everything the script does is appended to /tmp/bootstrapmate-postinstall.log. That file is
the first place to look when a package installed but nothing happened.
The bootstrap run is driven by one daemon:
| Label | com.github.bootstrapmate |
| Plist | /Library/LaunchDaemons/com.github.bootstrapmate.plist |
| Program | /Applications/Utilities/BootstrapMate.app/Contents/MacOS/managedbootstrapinstall |
| Keys |
RunAtLoad true, AbandonProcessGroup true |
ProgramArguments carries the executable path and nothing else. There is no KeepAlive,
and no StandardOutPath or StandardErrorPath — all output goes to the tool's own log
directory.
AbandonProcessGroup is what lets donotwait items keep running after the daemon boots
itself out at the end of the run.
The daemon is one-shot. At the end of every run, successful or not, BootstrapMate deletes
its own plist and runs launchctl bootout system/com.github.bootstrapmate. An absent plist on
a provisioned Mac is the normal, expected state — not evidence of a failed install.
BootstrapMate is delivered like any other enrollment-time package. In vendor-neutral terms you need two things assigned to the device at Automated Device Enrollment:
-
A bootstrap package. Deliver the signed
.pkgthrough whichever mechanism your MDM offers for installing a package during enrollment — anInstallEnterpriseApplicationcommand, a "custom bootstrap package", or the equivalent pre-stage package slot. The postinstall loads the daemon, andRunAtLoadstarts the run immediately. -
A configuration profile carrying the preference domain
com.github.bootstrapmate. This is how the run learns what to install.
Because the daemon passes no arguments, the manifest URL for a daemon-driven run must come
from the profile. Set url (aliases jsonUrl, jsonurl, JsonUrl, ConfigURL,
ManifestURL) in a com.apple.configuration.plist payload scoped to the system, with
PreferenceDomain set to com.github.bootstrapmate. If the manifest is behind an
authenticated endpoint, add headers in the same payload. See
Preferences for the full key list and
Serving Manifests and Packages for hosting.
If the profile has not landed by the time the daemon starts, BootstrapMate waits for it: with
no --jsonurl and no usable configuration it re-reads managed preferences once a second for
up to 300 seconds before logging Management configuration not received within 300s and
continuing. If no manifest URL has arrived by then the run exits 1.
The daemon removes itself after a run, so rebooting alone will not repeat the bootstrap. Pick one of these.
Reinstall the package. The postinstall lays the plist back down and loads it, which starts a fresh run:
sudo installer -pkg BootstrapMate-<version>.pkg -target /
Or restore the plist yourself from a copy and load it:
sudo launchctl load -w /Library/LaunchDaemons/com.github.bootstrapmate.plist
Or invoke the CLI directly, which is the better option when you are testing a manifest. This bypasses the profile entirely:
sudo /usr/local/bootstrapmate/managedbootstrapinstall --jsonurl https://example.com/bootstrap/manifest.yaml --verbose
Add --dry-run --no-dialog to walk the manifest without installing anything. Every flag is
listed in Command Line Reference.
Note that a run invoked this way still performs the one-shot cleanup at the end, so it will delete the LaunchDaemon plist if one is present.
Two mechanisms make a re-run cheaper rather than repeating work: a payload whose SHA-256
already matches is not downloaded again, and a package whose packageid and version are
both set and already satisfied by a pkgutil receipt is not reinstalled.