Skip to content

Serving Manifests and Packages

Rod Christiansen edited this page Sep 3, 2026 · 1 revision

Serving Manifests and Packages

BootstrapMate needs one manifest URL and one URL per payload, all reachable from a Mac that is still inside Setup Assistant. This page covers what the host has to do, what BootstrapMate does and does not check about the response, and how to prove a host is serving correctly before you point a fleet at it.

URL requirements

The manifest URL comes from the url preference or --jsonurl; each payload URL comes from the url field of its manifest item. Both are fetched with a plain HTTP GET.

The file extension of the URL path decides how the manifest is parsed. A path ending .yaml or .yml is parsed as YAML, .json is parsed as JSON, and anything else is tried as JSON first and YAML second. Query parameters do not interfere with that detection, so a signed URL such as https://example.com/bootstrap/manifest.json?token=… still parses as JSON. The response's Content-Type is never consulted, so a host that serves the manifest as application/octet-stream still works.

Payload URLs have no extension requirement. Each item's file field decides where the bytes land on disk, and the hash field decides whether they are accepted.

HTTPS

Serve everything over HTTPS. Nothing in the tool enforces it, and there is no certificate pinning, but the manifest is neither signed nor hash-checked and its contents are executed as root, so a manifest fetched in plaintext is an open invitation. See Security and Package Verification.

Redirects

Redirects are always followed, on the manifest and on payloads alike. The --follow-redirects flag and the matching followRedirects preference and manifest field are read but have no effect in either direction, so you cannot turn redirect-following off. See Troubleshooting and Gotchas.

Two things follow from that. A host that redirects https://example.com/bootstrap/manifest to https://example.com/bootstrap/manifest.json works, but the parser sees the requested URL, not the final one, so the extension on the URL you configure is the one that matters. And a redirect to a different origin is followed silently, taking the Authorization header with it.

Authentication and custom headers

BootstrapMate supports exactly one credential for downloads: the complete value of an Authorization header, supplied as --headers or the headers preference. Set it to the whole header value, including the scheme:

Bearer YOUR_AUTH_TOKEN_HERE
Basic BASE64_OF_USER_COLON_PASSWORD

There is no way to send any other header, no way to send more than one credential, and no way to scope a credential to a host. The same value is attached to the manifest request and to every payload download in the manifest, whichever host the payload URL names. If some payloads live on a host that should not see your credential, use pre-signed URLs for those rather than a shared header.

Reporting uses its own credential, the reportingHeader preference, and is never sent to download hosts. See Preferences.

Private origins and CDNs

Both patterns work, with caveats.

Caching is disabled at the client: the URL cache is off and requests are made with reloadIgnoringLocalAndRemoteCacheData, so BootstrapMate always asks the origin chain for fresh bytes. This matters because the per-item SHA-256 is the only signal that a payload changed. If your CDN serves a stale manifest anyway, Macs compare new payloads against old hashes, decide the files they already have are still valid, and never re-download. Purge the manifest on the CDN whenever you publish a new one, or serve the manifest from the origin and only the payloads from the CDN.

If the origin is private and access is granted by a rewritten header or a signed URL at the edge, remember that BootstrapMate sends only the one Authorization value and follows every redirect. A CDN rule that swaps an inbound header for an origin credential is compatible with that; a scheme that needs a second header, or a challenge-response, is not.

Two operational constraints are worth planning around. Each payload is downloaded into memory in full before being written to disk, so very large payloads are resident in RAM during the download. And each download has a 120-second ceiling, with the manifest fetch capped at 60 seconds, so a slow origin fails the item rather than stalling the run.

Verifying a host

Run these from a machine on a network like the one your Macs enroll from, not from inside the hosting environment.

Confirm the manifest returns 200 and see what the host claims it is. Content-Type is not used by BootstrapMate, but a text/html type here usually means you are looking at an error page or a login redirect.

curl -sS -D - -o /dev/null -L https://example.com/bootstrap/manifest.json

Confirm the body is the manifest and not an error page, and that it parses.

curl -sSL https://example.com/bootstrap/manifest.json | python3 -m json.tool | head -40

If the manifest is behind a credential, confirm it works with the exact header value you will put in the profile.

curl -sS -o /dev/null -w '%{http_code}\n' -L -H "Authorization: Bearer YOUR_AUTH_TOKEN_HERE" https://example.com/bootstrap/manifest.json

Confirm every payload URL is reachable and returns 200, using the same credential.

curl -sS -o /dev/null -w '%{http_code} %{size_download} %{url_effective}\n' -L -H "Authorization: Bearer YOUR_AUTH_TOKEN_HERE" https://example.com/bootstrap/payloads/Example-1.0.pkg

Confirm a payload's digest matches the hash in the manifest. Any mismatch here is the same mismatch a Mac will hit.

curl -sSL -H "Authorization: Bearer YOUR_AUTH_TOKEN_HERE" -o /tmp/Example-1.0.pkg https://example.com/bootstrap/payloads/Example-1.0.pkg && shasum -a 256 /tmp/Example-1.0.pkg

Then run the whole thing end to end without changing the Mac.

sudo /usr/local/bootstrapmate/managedbootstrapinstall --jsonurl https://example.com/bootstrap/manifest.json --verbose --dry-run --no-dialog

Checklist

  • Manifest URL path ends in .json, .yaml or .yml.
  • Manifest and every payload return 200 over HTTPS with no interactive login in the way.
  • Every payload's SHA-256 matches the hash in the manifest.
  • The manifest is purged from any CDN cache whenever it is republished.
  • Packages are signed by the Team ID you configured, or the exceptions are declared per item.
  • If a credential is required, it is a single Authorization value that works for the manifest and for every payload host in the manifest.
  • The URLs resolve on the network the Macs enroll from, including any split-DNS or filtering that applies before a user has logged in.

See also

Clone this wiki locally