-
Notifications
You must be signed in to change notification settings - Fork 0
Logging and Reporting
Every BootstrapMate run writes one log file, updates two status artefacts, and optionally POSTs a run summary to an endpoint you nominate. This page is the reference for all of it, and ends with the sequence of lines a healthy run produces.
/Library/Managed Bootstrap/logs/
One file per process run, named from the run's start time in local time:
2026-09-01-131514.log
The pattern is yyyy-MM-dd-HHmmss followed by .log. There is no size-based rotation and no
appending — a new run means a new file. The GUI's Logs tab reads this exact directory.
The directory is created by the package's postinstall, and again by the CLI at startup if it
is missing. If it cannot be created the CLI writes
bootstrapmate: failed to create log directory <dir>: <err> to stderr and continues with no
log file. There is no fallback log location.
At logger initialisation, every file in the log directory ending in .log whose modification
date is older than 30 days is deleted. The sweep is non-recursive and error-tolerant, and logs
its own result as Removed N log file(s) older than 30 days.
[2026-09-01 13:15:14] INFO Session started
[2026-09-01 13:15:14] ERROR Failed to load manifest
[2026-09-01 13:15:14] WARN Retrying
[2026-09-01 13:15:14] DEBUG Detail
The fields are: a bracketed timestamp yyyy-MM-dd HH:mm:ss in local time, then the level
label padded to five characters, then the message. Every physical line gets its own timestamp,
blank lines are dropped, and \r\n and \r are normalised.
Internally there are five: debug, info, warning, error, success. On disk only four
labels appear — DEBUG, INFO, WARN, ERROR. Success is written as INFO.
The console gets icon prefixes instead: [~] debug, [i] info, [!] warning, [x] error,
[+] success. --silent suppresses console output entirely; [~] lines only appear with
--verbose. Console verbosity comes from the CLI flags — setting verboseMode or
silentMode by profile does not change it, because the logger is initialised before those
preferences are read.
Inside the message text you will also see [SECTION], [PROGRESS], [SUB-PROGRESS],
[SUCCESS], [SKIPPED], [COMPLETION], === <title> === headings, and — for output
captured from a child script — [OUTPUT] <scriptname>: . Child stdout is logged at INFO,
child stderr at WARN.
The header is written at init and records Version:, Session Start Time:, Process ID:,
User:, Machine:, OS:, Architecture: (ARM64 or X64), Working Directory:,
Command Line:, Verbose Console: and Silent Mode:. The footer records the duration.
The Version: value in the header is not the build version — see
Troubleshooting and Gotchas.
Tail the newest log file:
tail -f "$(ls -t /Library/Managed\ Bootstrap/logs/*.log | head -1)"
Everything also goes to the unified log under subsystem com.github.bootstrapmate, category
general, so you can stream it without knowing the filename:
log stream --predicate 'subsystem == "com.github.bootstrapmate"'
Or read back a window that has already passed:
log show --predicate 'subsystem == "com.github.bootstrapmate"' --last 1h
Levels map to debug, info (info and success), default (warning) and error.
BootstrapMate writes per-phase status, keyed by the phase names Preflight,
SetupAssistant and Userland, to two places.
/Library/Managed Bootstrap/status.json — pretty-printed JSON with sorted keys, one entry per
phase, each carrying stage, startTime, completionTime, exitCode, version, phase,
architecture, bootstrapUrl, lastError and runId.
/Library/Preferences/com.github.bootstrapmate.plist — the same information as an XML plist,
with capitalised key names (Stage, StartTime, CompletionTime, ExitCode, Version,
Phase, Architecture, BootstrapUrl, LastError, RunId).
Stage takes one of Starting, Running, Completed, Failed, Skipped. Timestamps are
yyyy-MM-dd HH:mm:ss.
On a successful run the plist is then overwritten with only LastRunVersion,
LastUpdated and Architecture, so the per-phase detail is not there afterwards. This is
listed under known issues in
Troubleshooting and Gotchas.
If reportingUrl is configured — by profile, or with --reporting-url — BootstrapMate POSTs
one JSON run summary after the dialog closes and before cleanup. It fires once, and a failure
to report never fails the run.
The request uses Content-Type: application/json and
User-Agent: BootstrapMate/<version>, with a 15-second request timeout and a 20-second bound
on the wait. If reportingHeader is set, its value is sent as the Authorization header.
The payload has sorted keys and these fields:
| Field | Value |
|---|---|
tool |
"BootstrapMate" |
platform |
"macOS" |
schemaVersion |
1 |
version |
the tool's version string |
runId |
UUID generated for this run |
success |
boolean |
startTime / endTime
|
ISO-8601 |
durationSeconds |
integer, rounded |
architecture |
ARM64 or X64
|
hostname |
the host name |
serialNumber |
the platform serial, or "" if unavailable |
manifestUrl |
the configured manifest URL |
phases |
per-phase stage, exitCode, startTime, completionTime, lastError
|
The payload is vendor-neutral; any service that accepts a JSON POST can consume it.
The three log lines to grep for are
Posting run summary to reporting endpoint: <scheme>://<host><path> (query string and
userinfo are redacted), then either Run summary reported (HTTP 2xx) or
Reporting endpoint returned HTTP <code> / Reporting POST failed: <err>.
On a successful run the phases object arrives empty. That is a known issue, not a
misconfigured endpoint — see
Troubleshooting and Gotchas.
In order, in the log file:
-
=== BootstrapMate Session Started ===and the header block. -
BootstrapMate v<version> started. -
Network is available— or the timeout line, which is not by itself fatal. - If the data volume was not ready,
Data volume is now writable after Ns. - If configuration came from a profile, the wait ends quietly; if it did not, you would see
Still waiting for management config... (Ns elapsed). - The manifest loads. No
Failed to load manifest from <url>line. - Preflight: either nothing (no preflight item, phase
Skipped) orPreflight script exited N - continuing with bootstrap.Preflight script exited 0means the device was deemed already configured and the whole bootstrap was intentionally skipped. - Setup Assistant items, each downloading and installing. Re-runs show
Already have valid file: <path>. Skipping re-download.for payloads already on disk. NoRefusing to install, noAll retries failed. -
Waiting for user sessionin the log, followed by a matching user-session line once somebody logs in. AWaiting for user sessionwith nothing after it is the stuck case. - Userland items run.
- If reporting is configured:
Posting run summary to reporting endpoint: …thenRun summary reported (HTTP 2xx). -
Session complete — removing one-shot LaunchDaemon com.github.bootstrapmateandRemoved /Library/LaunchDaemons/com.github.bootstrapmate.plist. -
=== BootstrapMate Session Ended === (Duration: N.Ns)and the total duration line.
A quick pass for anything wrong:
grep -E '\] ERROR |\] WARN ' "$(ls -t /Library/Managed\ Bootstrap/logs/*.log | head -1)"