Skip to content
Rod Christiansen edited this page Sep 5, 2026 · 1 revision

Stages

BootstrapMate for Windows recognises two stages, and calls them phases: setupassistant and userland. They are the only two top-level keys in a manifest. Read this page before you decide which of your items belongs where, because the split does less than the names suggest.

The two phases

Phase Manifest key Order Status key
Setup Assistant setupassistant First HKLM\SOFTWARE\Cimian\BootstrapMate\Status\SetupAssistant
Userland userland Second HKLM\SOFTWARE\Cimian\BootstrapMate\Status\Userland

setupassistant runs to completion, then userland starts. Both run inside one invocation of managedbootstrapinstall.exe, in the same await chain, with nothing in between. There is no wait for a user session, no reboot, and no second process.

A phase whose key is absent from the manifest is recorded as Skipped and the run moves on. A phase that is present transitions StartingRunningCompleted, or Failed if the phase itself throws. Individual item failures do not fail the phase; they are caught and the loop continues to the next item, so a phase can report Completed with every item failed. See Logging and Reporting for the full status contract.

Ordering inside a phase

Manifest order is not execution order. Before running a phase, items are sorted by a priority derived from type and name:

Priority Matches
1 type is msi or exe
2 type is nupkg
3 type is powershell or ps1
5 Any other or unreadable type
10 powershell/ps1 whose name contains cleanup, clean, wipe, remove, delete, purge, nuclear or maintenance

The sort is stable, so manifest order is preserved within a band. When the sort changes anything, the log says Optimizing package installation order to prevent dependency conflicts.

There is no dependency graph. If item B needs item A, the only lever you have is the priority table above — and the name-based rule at priority 10 will catch a script called "Remove Legacy Agent" whether you meant it to or not.

Security context

Every item runs with the token of the process that invoked BootstrapMate. Child installers are started with UseShellExecute = false, so they inherit that token directly. The CLI's application manifest requests requireAdministrator, and it re-checks for the Administrators role at startup, so in practice everything runs elevated.

In a normal MSI deployment that means SYSTEM, in both phases. The MSI's install-time custom action runs schtasks /Run on a task registered to run as SYSTEM, and the daily 03:00 self-heal task runs as SYSTEM too. Nothing in the userland phase runs as the logged-in user. If you need per-user work, hand it to the ongoing management agent — see Handoff to Cimian.

An unelevated --silent run always exits 1, because it refuses to raise a UAC prompt it cannot display.

The phase names are labels, not detection

There is no OOBE (Out-of-Box Experience) or ESP (Enrollment Status Page) detection anywhere in this build. Nothing probes enrolment state, session 0, the ESP shell or a console user, and nothing waits for login. ManagementDetector, despite the name, reads managed settings from the registry and reports nothing about MDM enrolment.

So what makes a phase a "device phase" is when you invoke the CLI, not anything the CLI decides. Delivering the MSI as a required Win32 app that installs during the ESP gives you a device-context run of both phases; the daily task gives you another device-context run of both phases every morning. If you want work to land after a user logs in, you need something else to invoke BootstrapMate at that point.

Given that, the useful way to read the two phases on Windows is as a coarse ordering mechanism with separate status reporting, not as two different environments. Put the things everything else depends on — the management agent, the installer helper, runtimes — in setupassistant, and the rest in userland.

Mapping to the macOS stage model

The macOS build calls these stages, uses the same two key names, and adds a third:

macOS Windows Difference
preflight none A single script that can end the run before it starts. Not implemented here; the key is ignored
setupassistant setupassistant Same key, same position. macOS runs it before a user session exists; Windows does not check
userland userland macOS blocks until a console user exists, with no timeout, then runs. Windows never waits and never checks

The other divergence is vocabulary: macOS documentation and code say stage, the Windows code and registry say phase (InstallationPhase, and a Phase value under each status key). The JSON key names are identical, so the two are the same word in the manifest and different words everywhere else. See https://github.com/bootstrapmate/bootstrapmate-macintosh/wiki/Stages.

See also

Clone this wiki locally